Unify our diagnostic printing for errors of the form, "we didn't like

what we found when we looked into <blah>", where <blah> is a
DeclContext*. We can now format DeclContext*'s in nice ways, e.g.,
"namespace N", "the global namespace", "'class Foo'".

This is part of PR3990, but we're not quite there yet.

llvm-svn: 84028
This commit is contained in:
Douglas Gregor
2009-10-13 21:16:44 +00:00
parent 18a956cb4a
commit e40876a50c
14 changed files with 147 additions and 127 deletions

View File

@@ -15,6 +15,7 @@
#define LLVM_CLANG_DIAGNOSTIC_H
#include "clang/Basic/SourceLocation.h"
#include "llvm/Support/type_traits.h"
#include <string>
#include <vector>
#include <cassert>
@@ -24,6 +25,7 @@ namespace llvm {
}
namespace clang {
class DeclContext;
class DiagnosticBuilder;
class DiagnosticClient;
class IdentifierInfo;
@@ -158,7 +160,8 @@ public:
ak_qualtype, // QualType
ak_declarationname, // DeclarationName
ak_nameddecl, // NamedDecl *
ak_nestednamespec // NestedNameSpecifier *
ak_nestednamespec, // NestedNameSpecifier *
ak_declcontext // DeclContext *
};
private:
@@ -613,6 +616,20 @@ inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
return DB;
}
// Adds a DeclContext to the diagnostic. The enable_if template magic is here
// so that we only match those arguments that are (statically) DeclContexts;
// other arguments that derive from DeclContext (e.g., RecordDecls) will not
// match.
template<typename T>
inline
typename llvm::enable_if<llvm::is_same<T, DeclContext>,
const DiagnosticBuilder &>::type
operator<<(const DiagnosticBuilder &DB, T *DC) {
DB.AddTaggedVal(reinterpret_cast<intptr_t>(DC),
Diagnostic::ak_declcontext);
return DB;
}
inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const SourceRange &R) {
DB.AddSourceRange(R);

View File

@@ -1107,10 +1107,8 @@ def note_explicit_instantiation_candidate : Note<
// C++ typename-specifiers
def err_typename_nested_not_found : Error<"no type named %0 in %1">;
def err_typename_nested_not_found_global : Error<
"no type named %0 in the global namespace">;
def err_typename_nested_not_type : Error<
"typename specifier refers to non-type member %0">;
"typename specifier refers to non-type member %0 in %1">;
def note_typename_refers_here : Note<
"referenced member %0 is declared here">;
@@ -1405,17 +1403,11 @@ def warn_subscript_is_char : Warning<"array subscript is of type 'char'">,
InGroup<CharSubscript>, DefaultIgnore;
def err_typecheck_incomplete_tag : Error<"incomplete definition of type %0">;
def err_typecheck_no_member_deprecated : Error<"no member named %0">;
def err_typecheck_record_no_member : Error<
"no member named %0 in %select{struct|union|class|enum}1 %q2">;
def err_typecheck_namespace_no_member : Error<
"no member named %0 in namespace %q1">;
def err_typecheck_global_namespace_no_member : Error<
"no member named %0 in the global namespace">;
def err_no_member : Error<"no member named %0 in %1">;
def err_member_redeclared : Error<"class member cannot be redeclared">;
def err_member_def_does_not_match : Error<
"out-of-line definition does not match any declaration in %0">;
"out-of-line definition of %0 does not match any declaration in %1">;
def err_nonstatic_member_out_of_line : Error<
"non-static data member defined out-of-line">;
def err_qualified_typedef_declarator : Error<

View File

@@ -802,6 +802,7 @@ FormatDiagnostic(llvm::SmallVectorImpl<char> &OutStr) const {
case Diagnostic::ak_declarationname:
case Diagnostic::ak_nameddecl:
case Diagnostic::ak_nestednamespec:
case Diagnostic::ak_declcontext:
getDiags()->ConvertArgToString(getArgKind(ArgNo), getRawArg(ArgNo),
Modifier, ModifierLen,
Argument, ArgumentLen, OutStr);

View File

@@ -23,6 +23,54 @@
#include "clang/Basic/TargetInfo.h"
using namespace clang;
/// \brief Convert the given type to a string suitable for printing as part of
/// a diagnostic.
///
/// \param Context the context in which the type was allocated
/// \param Ty the type to print
static std::string ConvertTypeToDiagnosticString(ASTContext &Context,
QualType Ty) {
// FIXME: Playing with std::string is really slow.
std::string S = Ty.getAsString(Context.PrintingPolicy);
// If this is a sugared type (like a typedef, typeof, etc), then unwrap one
// level of the sugar so that the type is more obvious to the user.
QualType DesugaredTy = Ty.getDesugaredType();
if (Ty != DesugaredTy &&
// If the desugared type is a vector type, we don't want to expand it,
// it will turn into an attribute mess. People want their "vec4".
!isa<VectorType>(DesugaredTy) &&
// Don't aka just because we saw an elaborated type...
(!isa<ElaboratedType>(Ty) ||
cast<ElaboratedType>(Ty)->desugar() != DesugaredTy) &&
// ...or a qualified name type...
(!isa<QualifiedNameType>(Ty) ||
cast<QualifiedNameType>(Ty)->desugar() != DesugaredTy) &&
// ...or a non-dependent template specialization.
(!isa<TemplateSpecializationType>(Ty) || Ty->isDependentType()) &&
// Don't desugar magic Objective-C types.
Ty.getUnqualifiedType() != Context.getObjCIdType() &&
Ty.getUnqualifiedType() != Context.getObjCClassType() &&
Ty.getUnqualifiedType() != Context.getObjCSelType() &&
Ty.getUnqualifiedType() != Context.getObjCProtoType() &&
// Not va_list.
Ty.getUnqualifiedType() != Context.getBuiltinVaListType()) {
S = "'"+S+"' (aka '";
S += DesugaredTy.getAsString(Context.PrintingPolicy);
S += "')";
return S;
}
S = "'" + S + "'";
return S;
}
/// ConvertQualTypeToStringFn - This function is used to pretty print the
/// specified QualType as a string in diagnostics.
static void ConvertArgToStringFn(Diagnostic::ArgumentKind Kind, intptr_t Val,
@@ -33,50 +81,14 @@ static void ConvertArgToStringFn(Diagnostic::ArgumentKind Kind, intptr_t Val,
ASTContext &Context = *static_cast<ASTContext*>(Cookie);
std::string S;
bool NeedQuotes = true;
if (Kind == Diagnostic::ak_qualtype) {
assert(ModLen == 0 && ArgLen == 0 &&
"Invalid modifier for QualType argument");
QualType Ty(QualType::getFromOpaquePtr(reinterpret_cast<void*>(Val)));
// FIXME: Playing with std::string is really slow.
S = Ty.getAsString(Context.PrintingPolicy);
// If this is a sugared type (like a typedef, typeof, etc), then unwrap one
// level of the sugar so that the type is more obvious to the user.
QualType DesugaredTy = Ty.getDesugaredType();
if (Ty != DesugaredTy &&
// If the desugared type is a vector type, we don't want to expand it,
// it will turn into an attribute mess. People want their "vec4".
!isa<VectorType>(DesugaredTy) &&
// Don't aka just because we saw an elaborated type...
(!isa<ElaboratedType>(Ty) ||
cast<ElaboratedType>(Ty)->desugar() != DesugaredTy) &&
// ...or a qualified name type...
(!isa<QualifiedNameType>(Ty) ||
cast<QualifiedNameType>(Ty)->desugar() != DesugaredTy) &&
// ...or a non-dependent template specialization.
(!isa<TemplateSpecializationType>(Ty) || Ty->isDependentType()) &&
// Don't desugar magic Objective-C types.
Ty.getUnqualifiedType() != Context.getObjCIdType() &&
Ty.getUnqualifiedType() != Context.getObjCClassType() &&
Ty.getUnqualifiedType() != Context.getObjCSelType() &&
Ty.getUnqualifiedType() != Context.getObjCProtoType() &&
// Not va_list.
Ty.getUnqualifiedType() != Context.getBuiltinVaListType()) {
S = "'"+S+"' (aka '";
S += DesugaredTy.getAsString(Context.PrintingPolicy);
S += "')";
Output.append(S.begin(), S.end());
return;
}
S = ConvertTypeToDiagnosticString(Context, Ty);
NeedQuotes = false;
} else if (Kind == Diagnostic::ak_declarationname) {
DeclarationName N = DeclarationName::getFromOpaqueInteger(Val);
@@ -101,16 +113,50 @@ static void ConvertArgToStringFn(Diagnostic::ArgumentKind Kind, intptr_t Val,
reinterpret_cast<NamedDecl*>(Val)->getNameForDiagnostic(S,
Context.PrintingPolicy,
Qualified);
} else {
} else if (Kind == Diagnostic::ak_nestednamespec) {
llvm::raw_string_ostream OS(S);
assert(Kind == Diagnostic::ak_nestednamespec);
reinterpret_cast<NestedNameSpecifier*> (Val)->print(OS,
Context.PrintingPolicy);
} else {
assert(Kind == Diagnostic::ak_declcontext);
DeclContext *DC = reinterpret_cast<DeclContext *> (Val);
NeedQuotes = false;
if (!DC) {
assert(false && "Should never have a null declaration context");
S = "unknown context";
} else if (DC->isTranslationUnit()) {
// FIXME: Get these strings from some localized place
if (Context.getLangOptions().CPlusPlus)
S = "the global namespace";
else
S = "the global scope";
} else if (TypeDecl *Type = dyn_cast<TypeDecl>(DC)) {
S = ConvertTypeToDiagnosticString(Context, Context.getTypeDeclType(Type));
NeedQuotes = false;
} else {
// FIXME: Get these strings from some localized place
NamedDecl *ND = cast<NamedDecl>(DC);
if (isa<NamespaceDecl>(ND))
S += "namespace ";
else if (isa<ObjCMethodDecl>(ND))
S += "method ";
else if (isa<FunctionDecl>(ND))
S += "function ";
S += "'";
ND->getNameForDiagnostic(S, Context.PrintingPolicy, true);
S += "'";
NeedQuotes = false;
}
}
Output.push_back('\'');
if (NeedQuotes)
Output.push_back('\'');
Output.append(S.begin(), S.end());
Output.push_back('\'');
if (NeedQuotes)
Output.push_back('\'');
}
@@ -363,31 +409,6 @@ NamedDecl *Sema::getCurFunctionOrMethodDecl() {
return 0;
}
void Sema::DiagnoseMissingMember(SourceLocation MemberLoc,
DeclarationName Member,
NestedNameSpecifier *NNS, SourceRange Range) {
switch (NNS->getKind()) {
default: assert(0 && "Unexpected nested name specifier kind!");
case NestedNameSpecifier::TypeSpec: {
const Type *Ty = Context.getCanonicalType(NNS->getAsType());
RecordDecl *RD = cast<RecordType>(Ty)->getDecl();
Diag(MemberLoc, diag::err_typecheck_record_no_member)
<< Member << RD->getTagKind() << RD << Range;
break;
}
case NestedNameSpecifier::Namespace: {
Diag(MemberLoc, diag::err_typecheck_namespace_no_member)
<< Member << NNS->getAsNamespace() << Range;
break;
}
case NestedNameSpecifier::Global: {
Diag(MemberLoc, diag::err_typecheck_global_namespace_no_member)
<< Member << Range;
break;
}
}
}
Sema::SemaDiagnosticBuilder::~SemaDiagnosticBuilder() {
if (!this->Emit())
return;

View File

@@ -3720,9 +3720,6 @@ public:
QualType FieldTy, const Expr *BitWidth,
bool *ZeroWidth = 0);
void DiagnoseMissingMember(SourceLocation MemberLoc, DeclarationName Member,
NestedNameSpecifier *NNS, SourceRange Range);
/// adjustFunctionParamType - Converts the type of a function parameter to a
// type that can be passed as an argument type to
/// ASTContext::getFunctionType.

View File

@@ -471,9 +471,7 @@ Sema::CXXScopeTy *Sema::BuildCXXNestedNameSpecifier(Scope *S,
if (SD)
DiagID = diag::err_expected_class_or_namespace;
else if (SS.isSet()) {
DiagnoseMissingMember(IdLoc, DeclarationName(&II),
(NestedNameSpecifier *)SS.getScopeRep(),
SS.getRange());
Diag(IdLoc, diag::err_no_member) << &II << LookupCtx << SS.getRange();
return 0;
} else
DiagID = diag::err_undeclared_var_use;

View File

@@ -2229,10 +2229,9 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC,
}
} else if (D.getCXXScopeSpec().isSet()) {
// No previous declaration in the qualifying scope.
NestedNameSpecifier *NNS =
(NestedNameSpecifier *)D.getCXXScopeSpec().getScopeRep();
DiagnoseMissingMember(D.getIdentifierLoc(), Name, NNS,
D.getCXXScopeSpec().getRange());
Diag(D.getIdentifierLoc(), diag::err_no_member)
<< Name << computeDeclContext(D.getCXXScopeSpec(), true)
<< D.getCXXScopeSpec().getRange();
NewVD->setInvalidDecl();
}
@@ -2844,7 +2843,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
// matches (e.g., those that differ only in cv-qualifiers and
// whether the parameter types are references).
Diag(D.getIdentifierLoc(), diag::err_member_def_does_not_match)
<< cast<NamedDecl>(DC) << D.getCXXScopeSpec().getRange();
<< Name << DC << D.getCXXScopeSpec().getRange();
NewFD->setInvalidDecl();
LookupResult Prev;

View File

@@ -2767,7 +2767,8 @@ NamedDecl *Sema::BuildUsingDeclaration(SourceLocation UsingLoc,
LookupQualifiedName(R, LookupContext, Name, LookupOrdinaryName);
if (R.empty()) {
DiagnoseMissingMember(IdentLoc, Name, NNS, SS.getRange());
Diag(IdentLoc, diag::err_no_member)
<< Name << LookupContext << SS.getRange();
return 0;
}

View File

@@ -802,12 +802,11 @@ Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc,
else {
// If this name wasn't predeclared and if this is not a function call,
// diagnose the problem.
if (SS && !SS->isEmpty()) {
DiagnoseMissingMember(Loc, Name,
(NestedNameSpecifier *)SS->getScopeRep(),
SS->getRange());
return ExprError();
} else if (Name.getNameKind() == DeclarationName::CXXOperatorName ||
if (SS && !SS->isEmpty())
return ExprError(Diag(Loc, diag::err_no_member)
<< Name << computeDeclContext(*SS, false)
<< SS->getRange());
else if (Name.getNameKind() == DeclarationName::CXXOperatorName ||
Name.getNameKind() == DeclarationName::CXXConversionFunctionName)
return ExprError(Diag(Loc, diag::err_undeclared_use)
<< Name.getAsString());
@@ -2185,8 +2184,8 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
LookupQualifiedName(Result, DC, MemberName, LookupMemberName, false);
if (Result.empty())
return ExprError(Diag(MemberLoc, diag::err_typecheck_no_member_deprecated)
<< MemberName << BaseExpr->getSourceRange());
return ExprError(Diag(MemberLoc, diag::err_no_member)
<< MemberName << DC << BaseExpr->getSourceRange());
if (Result.isAmbiguous()) {
DiagnoseAmbiguousLookup(Result, MemberName, MemberLoc,
BaseExpr->getSourceRange());
@@ -5689,8 +5688,8 @@ Sema::OwningExprResult Sema::ActOnBuiltinOffsetOf(Scope *S,
= dyn_cast_or_null<FieldDecl>(R.getAsSingleDecl(Context));
// FIXME: Leaks Res
if (!MemberDecl)
return ExprError(Diag(BuiltinLoc, diag::err_typecheck_no_member_deprecated)
<< OC.U.IdentInfo << SourceRange(OC.LocStart, OC.LocEnd));
return ExprError(Diag(BuiltinLoc, diag::err_no_member)
<< OC.U.IdentInfo << RD << SourceRange(OC.LocStart, OC.LocEnd));
// FIXME: C++: Verify that MemberDecl isn't a static field.
// FIXME: Verify that MemberDecl isn't a bitfield.

View File

@@ -3903,10 +3903,7 @@ Sema::CheckTypenameType(NestedNameSpecifier *NNS, const IdentifierInfo &II,
Decl *Referenced = 0;
switch (Result.getKind()) {
case LookupResult::NotFound:
if (Ctx->isTranslationUnit())
DiagID = diag::err_typename_nested_not_found_global;
else
DiagID = diag::err_typename_nested_not_found;
DiagID = diag::err_typename_nested_not_found;
break;
case LookupResult::Found:
@@ -3933,10 +3930,7 @@ Sema::CheckTypenameType(NestedNameSpecifier *NNS, const IdentifierInfo &II,
// If we get here, it's because name lookup did not find a
// type. Emit an appropriate diagnostic and return an error.
if (NamedDecl *NamedCtx = dyn_cast<NamedDecl>(Ctx))
Diag(Range.getEnd(), DiagID) << Range << Name << NamedCtx;
else
Diag(Range.getEnd(), DiagID) << Range << Name;
Diag(Range.getEnd(), DiagID) << Range << Name << Ctx;
if (Referenced)
Diag(Referenced->getLocation(), diag::note_typename_refers_here)
<< Name;

View File

@@ -9,12 +9,12 @@ template<class T1> class A {
template<> template<class X>
class A<long>::B { };
// FIXME: If we make the explicit specialization of A<long>::B, above, into
// a specialization of A<int>::B, our diagnostic is correct but not very
// helpful.
template<> template<> template<class T>
void A<int>::B<double>::mf1(T t) { }
template<> template<> template<class T>
void A<long>::B<double>::mf1(T t) { } // expected-error{{does not match}}
// FIXME: This diagnostic could probably be better.
template<class Y> template<>
void A<Y>::B<double>::mf2() { } // expected-error{{does not refer}}

View File

@@ -9,7 +9,7 @@ namespace A {
void f() {
A::B::i; // expected-error {{no member named 'i' in namespace 'A::B'}}
A::B::C::i; // expected-error {{no member named 'i' in class 'A::B::C'}}
A::B::C::i; // expected-error {{no member named 'i' in 'class A::B::C'}}
::i; // expected-error {{no member named 'i' in the global namespace}}
}
@@ -19,18 +19,18 @@ namespace B {
void g() {
A::B::D::E; // expected-error {{no member named 'D' in namespace 'A::B'}}
B::B::C::D; // expected-error {{no member named 'C' in class 'B::B'}}
B::B::C::D; // expected-error {{no member named 'C' in 'class B::B'}}
::C::D; // expected-error {{no member named 'C' in the global namespace}}
}
int A::B::i = 10; // expected-error {{no member named 'i' in namespace 'A::B'}}
int A::B::C::i = 10; // expected-error {{no member named 'i' in class 'A::B::C'}}
int A::B::S::i = 10; // expected-error {{no member named 'i' in struct 'A::B::S'}}
int A::B::U::i = 10; // expected-error {{no member named 'i' in union 'A::B::U'}}
int A::B::C::i = 10; // expected-error {{no member named 'i' in 'class A::B::C'}}
int A::B::S::i = 10; // expected-error {{no member named 'i' in 'struct A::B::S'}}
int A::B::U::i = 10; // expected-error {{no member named 'i' in 'union A::B::U'}}
using A::B::D; // expected-error {{no member named 'D' in namespace 'A::B'}}
struct S : A::B::C {
using A::B::C::f; // expected-error {{no member named 'f' in class 'A::B::C'}}
using A::B::C::f; // expected-error {{no member named 'f' in 'class A::B::C'}}
};

View File

@@ -35,9 +35,9 @@ class C2 {
int x;
};
void C2::m() const { } // expected-error{{out-of-line definition does not match any declaration in 'C2'}}
void C2::m() const { } // expected-error{{out-of-line definition of 'm' does not match any declaration in 'class C2'}}
void C2::f(int) { } // expected-error{{out-of-line definition does not match any declaration in 'C2'}}
void C2::f(int) { } // expected-error{{out-of-line definition of 'f' does not match any declaration in 'class C2'}}
void C2::m() {
x = 0;
@@ -125,7 +125,7 @@ class Operators {
operator bool();
};
Operators Operators::operator+(const Operators&) { // expected-error{{out-of-line definition does not match any declaration in 'Operators'}}
Operators Operators::operator+(const Operators&) { // expected-error{{out-of-line definition of 'operator+' does not match any declaration in 'class Operators'}}
Operators ops;
return ops;
}
@@ -143,13 +143,13 @@ namespace A {
void g(int&); // expected-note{{member declaration nearly matches}}
}
void A::f() {} // expected-error{{out-of-line definition does not match any declaration in 'A'}}
void A::f() {} // expected-error{{out-of-line definition of 'f' does not match any declaration in namespace 'A'}}
void A::g(const int&) { } // expected-error{{out-of-line definition does not match any declaration in 'A'}}
void A::g(const int&) { } // expected-error{{out-of-line definition of 'g' does not match any declaration in namespace 'A'}}
struct Struct { };
void Struct::f() { } // expected-error{{out-of-line definition does not match any declaration in 'Struct'}}
void Struct::f() { } // expected-error{{out-of-line definition of 'f' does not match any declaration in 'struct Struct'}}
void global_func(int);
void global_func2(int);

View File

@@ -16,7 +16,7 @@ namespace N {
int i;
typename N::A::type *ip1 = &i;
typename N::B::type *ip2 = &i; // expected-error{{ no type named 'type' in 'B'}}
typename N::B::type *ip2 = &i; // expected-error{{no type named 'type' in 'struct N::B'}}
typename N::C::type *ip3 = &i; // expected-error{{typename specifier refers to non-type member 'type'}}
void test(double d) {
@@ -33,7 +33,8 @@ void test(double d) {
namespace N {
template<typename T>
struct X {
typedef typename T::type type; // expected-error 2{{no type named 'type' in 'B'}} \
typedef typename T::type type; // expected-error {{no type named 'type' in 'struct N::B'}} \
// expected-error {{no type named 'type' in 'struct B'}} \
// FIXME: location info for error above isn't very good \
// expected-error 2{{typename specifier refers to non-type member 'type'}} \
// expected-error{{type 'int' cannot be used prior to '::' because it has no members}}