aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2009-08-26 00:04:55 +0000
committerDouglas Gregor <dgregor@apple.com>2009-08-26 00:04:55 +0000
commitdacd434c49658286c380c7b4c357d76d53cb4aa1 (patch)
tree96445b18410c013f21aabb19f3fe17348fbfb225
parentec7738776ba68576db5d8316af399d1f983245ee (diff)
Improve diagnostics and recovery when the nested-name-specifier of a
qualified name does not actually refer into a class/class template/class template partial specialization. Improve printing of nested-name-specifiers to eliminate redudant qualifiers. Also, make it possible to output a nested-name-specifier through a DiagnosticBuilder, although there are relatively few places that will use this leeway. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@80056 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/AST/NestedNameSpecifier.h10
-rw-r--r--include/clang/Basic/Diagnostic.h3
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td4
-rw-r--r--lib/AST/NestedNameSpecifier.cpp32
-rw-r--r--lib/Basic/Diagnostic.cpp1
-rw-r--r--lib/Sema/Sema.cpp8
-rw-r--r--lib/Sema/SemaCXXScopeSpec.cpp22
-rw-r--r--lib/Sema/SemaDecl.cpp16
-rw-r--r--test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp2
-rw-r--r--test/SemaTemplate/nested-template.cpp4
10 files changed, 71 insertions, 31 deletions
diff --git a/include/clang/AST/NestedNameSpecifier.h b/include/clang/AST/NestedNameSpecifier.h
index a1c48ffda5..0c31dbbf1a 100644
--- a/include/clang/AST/NestedNameSpecifier.h
+++ b/include/clang/AST/NestedNameSpecifier.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H
#define LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H
+#include "clang/Basic/Diagnostic.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerIntPair.h"
@@ -179,6 +180,15 @@ public:
void dump(const LangOptions &LO);
};
+/// Insertion operator for diagnostics. This allows sending NestedNameSpecifiers
+/// into a diagnostic with <<.
+inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
+ NestedNameSpecifier *NNS) {
+ DB.AddTaggedVal(reinterpret_cast<intptr_t>(NNS),
+ Diagnostic::ak_nestednamespec);
+ return DB;
+}
+
}
#endif
diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h
index cdfa7e1157..8a09fa59ab 100644
--- a/include/clang/Basic/Diagnostic.h
+++ b/include/clang/Basic/Diagnostic.h
@@ -156,7 +156,8 @@ public:
ak_identifierinfo, // IdentifierInfo
ak_qualtype, // QualType
ak_declarationname, // DeclarationName
- ak_nameddecl // NamedDecl *
+ ak_nameddecl, // NamedDecl *
+ ak_nestednamespec // NestedNameSpecifier *
};
private:
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 8b717c5a7b..1230f5b9d4 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -889,8 +889,8 @@ def err_template_spec_extra_headers : Error<
"extraneous template parameter list in template specialization or "
"out-of-line template definition">;
def err_template_qualified_declarator_no_match : Error<
- "nested name specifier '%0' for declaration does not refer to a class "
- "template or class template partial specialization">;
+ "nested name specifier %0 for declaration does not refer into a class, "
+ "class template or class template partial specialization">;
def err_template_spec_decl_out_of_scope_global : Error<
"class template %select{|partial }0specialization of %1 must occur in the "
"global scope">;
diff --git a/lib/AST/NestedNameSpecifier.cpp b/lib/AST/NestedNameSpecifier.cpp
index 90ec4d33fd..0376f9db6b 100644
--- a/lib/AST/NestedNameSpecifier.cpp
+++ b/lib/AST/NestedNameSpecifier.cpp
@@ -131,15 +131,33 @@ NestedNameSpecifier::print(llvm::raw_ostream &OS,
std::string TypeStr;
Type *T = getAsType();
- // If this is a qualified name type, suppress the qualification:
- // it's part of our nested-name-specifier sequence anyway. FIXME:
- // We should be able to assert that this doesn't happen.
- if (const QualifiedNameType *QualT = dyn_cast<QualifiedNameType>(T))
- T = QualT->getNamedType().getTypePtr();
-
PrintingPolicy InnerPolicy(Policy);
InnerPolicy.SuppressTagKind = true;
- T->getAsStringInternal(TypeStr, InnerPolicy);
+
+ // Nested-name-specifiers are intended to contain minimally-qualified
+ // types. An actual QualifiedNameType will not occur, since we'll store
+ // just the type that is referred to in the nested-name-specifier (e.g.,
+ // a TypedefType, TagType, etc.). However, when we are dealing with
+ // dependent template-id types (e.g., Outer<T>::template Inner<U>),
+ // the type requires its own nested-name-specifier for uniqueness, so we
+ // suppress that nested-name-specifier during printing.
+ assert(!isa<QualifiedNameType>(T) &&
+ "Qualified name type in nested-name-specifier");
+ if (const TemplateSpecializationType *SpecType
+ = dyn_cast<TemplateSpecializationType>(T)) {
+ // Print the template name without its corresponding
+ // nested-name-specifier.
+ SpecType->getTemplateName().print(OS, InnerPolicy, true);
+
+ // Print the template argument list.
+ TypeStr = TemplateSpecializationType::PrintTemplateArgumentList(
+ SpecType->getArgs(),
+ SpecType->getNumArgs(),
+ InnerPolicy);
+ } else {
+ // Print the type normally
+ T->getAsStringInternal(TypeStr, InnerPolicy);
+ }
OS << TypeStr;
break;
}
diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp
index 6ba5907a34..922702fdf5 100644
--- a/lib/Basic/Diagnostic.cpp
+++ b/lib/Basic/Diagnostic.cpp
@@ -797,6 +797,7 @@ FormatDiagnostic(llvm::SmallVectorImpl<char> &OutStr) const {
case Diagnostic::ak_qualtype:
case Diagnostic::ak_declarationname:
case Diagnostic::ak_nameddecl:
+ case Diagnostic::ak_nestednamespec:
getDiags()->ConvertArgToString(getArgKind(ArgNo), getRawArg(ArgNo),
Modifier, ModifierLen,
Argument, ArgumentLen, OutStr);
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 5425c4fb5a..24c5e09f43 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -79,8 +79,7 @@ static void ConvertArgToStringFn(Diagnostic::ArgumentKind Kind, intptr_t Val,
else
assert(ModLen == 0 && ArgLen == 0 &&
"Invalid modifier for DeclarationName argument");
- } else {
- assert(Kind == Diagnostic::ak_nameddecl);
+ } else if (Kind == Diagnostic::ak_nameddecl) {
if (ModLen == 1 && Modifier[0] == 'q' && ArgLen == 0)
S = reinterpret_cast<NamedDecl*>(Val)->getQualifiedNameAsString();
else {
@@ -88,6 +87,11 @@ static void ConvertArgToStringFn(Diagnostic::ArgumentKind Kind, intptr_t Val,
"Invalid modifier for NamedDecl* argument");
S = reinterpret_cast<NamedDecl*>(Val)->getNameAsString();
}
+ } else {
+ llvm::raw_string_ostream OS(S);
+ assert(Kind == Diagnostic::ak_nestednamespec);
+ reinterpret_cast<NestedNameSpecifier*> (Val)->print(OS,
+ Context.PrintingPolicy);
}
Output.push_back('\'');
diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp
index 9c9bff8a08..3dac5d7297 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -79,17 +79,6 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
// The nested name specifier refers to a member of a class template.
return RecordT->getDecl();
}
-
- std::string NNSString;
- {
- llvm::raw_string_ostream OS(NNSString);
- NNS->print(OS, Context.PrintingPolicy);
- }
-
- // FIXME: Allow us to pass a nested-name-specifier to Diag?
- Diag(SS.getRange().getBegin(),
- diag::err_template_qualified_declarator_no_match)
- << NNSString << SS.getRange();
}
return 0;
@@ -298,6 +287,9 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
T.getTypePtr());
}
+ // FIXME: It would be nice to maintain the namespace alias name, then
+ // see through that alias when resolving the nested-name-specifier down to
+ // a declaration context.
if (NamespaceAliasDecl *Alias = dyn_cast<NamespaceAliasDecl>(SD))
return NestedNameSpecifier::Create(Context, Prefix,
Alias->getNamespace());
@@ -404,8 +396,6 @@ void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
if (DeclContext *DC = computeDeclContext(SS, true))
EnterDeclaratorContext(S, DC);
- else
- const_cast<CXXScopeSpec&>(SS).setScopeRep(0);
}
/// ActOnCXXExitDeclaratorScope - Called when a declarator that previously
@@ -415,8 +405,8 @@ void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
/// defining scope.
void Sema::ActOnCXXExitDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
- assert((SS.isInvalid() || S->getEntity() == computeDeclContext(SS, true)) &&
- "Context imbalance!");
- if (!SS.isInvalid())
+ if (SS.isInvalid())
+ return;
+ if (computeDeclContext(SS, true))
ExitDeclaratorContext(S);
}
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index d478c048b8..fec9a075ec 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1633,7 +1633,7 @@ Sema::HandleDeclarator(Scope *S, Declarator &D,
<< D.getDeclSpec().getSourceRange() << D.getSourceRange();
return DeclPtrTy();
}
-
+
// The scope passed in may not be a decl scope. Zip up the scope tree until
// we find one that is.
while ((S->getFlags() & Scope::DeclScope) == 0 ||
@@ -1701,7 +1701,19 @@ Sema::HandleDeclarator(Scope *S, Declarator &D,
D.getIdentifierLoc());
} else { // Something like "int foo::x;"
DC = computeDeclContext(D.getCXXScopeSpec(), true);
- // FIXME: RequireCompleteDeclContext(D.getCXXScopeSpec()); ?
+
+ if (!DC) {
+ // If we could not compute the declaration context, it's because the
+ // declaration context is dependent but does not refer to a class,
+ // class template, or class template partial specialization. Complain
+ // and return early, to avoid the coming semantic disaster.
+ Diag(D.getIdentifierLoc(),
+ diag::err_template_qualified_declarator_no_match)
+ << (NestedNameSpecifier*)D.getCXXScopeSpec().getScopeRep()
+ << D.getCXXScopeSpec().getRange();
+ return DeclPtrTy();
+ }
+
PrevDecl = LookupQualifiedName(DC, Name, LookupOrdinaryName, true);
// C++ 7.3.1.2p2:
diff --git a/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp b/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp
index 79f379aba1..28a5d98540 100644
--- a/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp
+++ b/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp
@@ -43,7 +43,7 @@ void X0<X, Y>::f3(size_type) const {
}
template<class X, class Y>
-void X0<Y, X>::f4() { } // expected-error{{does not refer to}}
+void X0<Y, X>::f4() { } // expected-error{{does not refer}}
// FIXME: error message should probably say, "redefinition of 'X0<T, U>::f0'"
// rather than just "redefinition of 'f0'"
diff --git a/test/SemaTemplate/nested-template.cpp b/test/SemaTemplate/nested-template.cpp
index 3a7634f53b..896eb72398 100644
--- a/test/SemaTemplate/nested-template.cpp
+++ b/test/SemaTemplate/nested-template.cpp
@@ -87,3 +87,7 @@ Y Outer<X>::Inner1<Y>::value2 = Y();
template<typename X>
template<typename Y>
Y Outer<X>::Inner1<Y>::ReallyInner::value3 = Y();
+
+template<typename X>
+template<typename Y>
+Y Outer<X>::Inner1<Y*>::ReallyInner::value4; // expected-error{{Outer<X>::Inner1<Y *>::ReallyInner::}}