Delete some more files.
This commit is contained in:
parent
7dc062fe0c
commit
e5557c18e5
1924
lib/AST/CFG.cpp
1924
lib/AST/CFG.cpp
File diff suppressed because it is too large
Load Diff
@ -1,322 +0,0 @@
|
||||
//===--- ResolveLocation.cpp - Source location resolver ---------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This defines the ResolveLocationInAST function, which resolves a
|
||||
// source location into a <Decl *, Stmt *> pair.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Frontend/Utils.h"
|
||||
#include "clang/AST/DeclVisitor.h"
|
||||
#include "clang/AST/StmtVisitor.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
|
||||
/// \brief Base for the LocResolver classes. Mostly does source range checking.
|
||||
class VISIBILITY_HIDDEN LocResolverBase {
|
||||
protected:
|
||||
ASTContext &Ctx;
|
||||
SourceLocation Loc;
|
||||
|
||||
Decl *Dcl;
|
||||
Stmt *Stm;
|
||||
bool PassedLoc;
|
||||
|
||||
/// \brief Checks whether Loc is in the source range of 'D'.
|
||||
///
|
||||
/// If it is, updates Dcl. If Loc is passed the source range, it sets
|
||||
/// PassedLoc, otherwise it does nothing.
|
||||
void CheckRange(Decl *D);
|
||||
|
||||
/// \brief Checks whether Loc is in the source range of 'Node'.
|
||||
///
|
||||
/// If it is, updates Stm. If Loc is passed the source range, it sets
|
||||
/// PassedLoc, otherwise it does nothing.
|
||||
void CheckRange(Stmt *Node);
|
||||
|
||||
/// \brief Updates the end source range to cover the full length of the token
|
||||
/// positioned at the end of the source range.
|
||||
///
|
||||
/// e.g.,
|
||||
/// @code
|
||||
/// int foo
|
||||
/// ^ ^
|
||||
/// @endcode
|
||||
/// will be updated to
|
||||
/// @code
|
||||
/// int foo
|
||||
/// ^ ^
|
||||
/// @endcode
|
||||
void FixRange(SourceRange &Range);
|
||||
|
||||
public:
|
||||
LocResolverBase(ASTContext &ctx, SourceLocation loc)
|
||||
: Ctx(ctx), Loc(loc), Dcl(0), Stm(0), PassedLoc(0) {}
|
||||
|
||||
/// \brief We found a AST node that corresponds to the source location.
|
||||
bool FoundIt() const { return Dcl != 0 || Stm != 0; }
|
||||
|
||||
/// \brief We either found a AST node or we passed the source location while
|
||||
/// searching.
|
||||
bool Finished() const { return FoundIt() || PassedLoc; }
|
||||
|
||||
Decl *getDecl() const { return Dcl; }
|
||||
Stmt *getStmt() const { return Stm; }
|
||||
|
||||
std::pair<Decl *, Stmt *> getResult() const {
|
||||
return std::make_pair(getDecl(), getStmt());
|
||||
}
|
||||
|
||||
/// \brief Debugging output.
|
||||
void print(Decl *D);
|
||||
/// \brief Debugging output.
|
||||
void print(Stmt *Node);
|
||||
};
|
||||
|
||||
/// \brief Searches a statement for the AST node that corresponds to a source
|
||||
/// location.
|
||||
class VISIBILITY_HIDDEN StmtLocResolver : public LocResolverBase,
|
||||
public StmtVisitor<StmtLocResolver> {
|
||||
public:
|
||||
StmtLocResolver(ASTContext &ctx, SourceLocation loc)
|
||||
: LocResolverBase(ctx, loc) {}
|
||||
|
||||
void VisitDeclStmt(DeclStmt *Node);
|
||||
void VisitStmt(Stmt *Node);
|
||||
};
|
||||
|
||||
/// \brief Searches a declaration for the AST node that corresponds to a source
|
||||
/// location.
|
||||
class VISIBILITY_HIDDEN DeclLocResolver : public LocResolverBase,
|
||||
public DeclVisitor<DeclLocResolver> {
|
||||
public:
|
||||
DeclLocResolver(ASTContext &ctx, SourceLocation loc)
|
||||
: LocResolverBase(ctx, loc) {}
|
||||
|
||||
void VisitDeclContext(DeclContext *DC);
|
||||
void VisitTranslationUnitDecl(TranslationUnitDecl *TU);
|
||||
void VisitVarDecl(VarDecl *D);
|
||||
void VisitFunctionDecl(FunctionDecl *D);
|
||||
void VisitDecl(Decl *D);
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void StmtLocResolver::VisitDeclStmt(DeclStmt *Node) {
|
||||
CheckRange(Node);
|
||||
if (!FoundIt())
|
||||
return;
|
||||
assert(Stm == Node && "Result not updated ?");
|
||||
|
||||
// Search all declarations of this DeclStmt. If we found the one corresponding
|
||||
// to the source location, update this StmtLocResolver's result.
|
||||
DeclLocResolver DLR(Ctx, Loc);
|
||||
for (DeclStmt::decl_iterator
|
||||
I = Node->decl_begin(), E = Node->decl_end(); I != E; ++I) {
|
||||
DLR.Visit(*I);
|
||||
if (DLR.Finished()) {
|
||||
if (DLR.FoundIt())
|
||||
llvm::tie(Dcl, Stm) = DLR.getResult();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StmtLocResolver::VisitStmt(Stmt *Node) {
|
||||
CheckRange(Node);
|
||||
if (!FoundIt())
|
||||
return;
|
||||
assert(Stm == Node && "Result not updated ?");
|
||||
|
||||
// Search the child statements.
|
||||
StmtLocResolver SLR(Ctx, Loc);
|
||||
for (Stmt::child_iterator
|
||||
I = Node->child_begin(), E = Node->child_end(); I != E; ++I) {
|
||||
SLR.Visit(*I);
|
||||
if (!SLR.Finished())
|
||||
continue;
|
||||
|
||||
// We either found it or we passed the source location.
|
||||
|
||||
if (SLR.FoundIt()) {
|
||||
// Only update Dcl if we found another more immediate 'parent' Decl for
|
||||
// the statement.
|
||||
if (SLR.getDecl())
|
||||
Dcl = SLR.getDecl();
|
||||
Stm = SLR.getStmt();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void DeclLocResolver::VisitDeclContext(DeclContext *DC) {
|
||||
DeclLocResolver DLR(Ctx, Loc);
|
||||
for (DeclContext::decl_iterator
|
||||
I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) {
|
||||
DLR.Visit(*I);
|
||||
if (DLR.Finished()) {
|
||||
if (DLR.FoundIt())
|
||||
llvm::tie(Dcl, Stm) = DLR.getResult();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeclLocResolver::VisitTranslationUnitDecl(TranslationUnitDecl *TU) {
|
||||
VisitDeclContext(TU);
|
||||
}
|
||||
|
||||
void DeclLocResolver::VisitFunctionDecl(FunctionDecl *D) {
|
||||
CheckRange(D);
|
||||
if (!FoundIt())
|
||||
return;
|
||||
assert(Dcl == D && "Result not updated ?");
|
||||
|
||||
// First, search through the parameters of the function.
|
||||
DeclLocResolver ParmRes(Ctx, Loc);
|
||||
for (FunctionDecl::param_iterator
|
||||
I = D->param_begin(), E = D->param_end(); I != E; ++I) {
|
||||
ParmRes.Visit(*I);
|
||||
if (ParmRes.Finished()) {
|
||||
if (ParmRes.FoundIt())
|
||||
llvm::tie(Dcl, Stm) = ParmRes.getResult();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't found the location in the parameters and we didn't get passed it.
|
||||
|
||||
// Second, search through the declarations that are part of the function.
|
||||
// If we find he location there, we won't have to search through its body.
|
||||
DeclLocResolver DLR(Ctx, Loc);
|
||||
DLR.VisitDeclContext(D);
|
||||
if (DLR.FoundIt()) {
|
||||
llvm::tie(Dcl, Stm) = DLR.getResult();
|
||||
return;
|
||||
}
|
||||
|
||||
// We didn't find a declaration that corresponds to the source location.
|
||||
|
||||
// Finally, search through the body of the function.
|
||||
if (D->isThisDeclarationADefinition()) {
|
||||
StmtLocResolver SLR(Ctx, Loc);
|
||||
SLR.Visit(D->getBody());
|
||||
if (SLR.FoundIt()) {
|
||||
llvm::tie(Dcl, Stm) = SLR.getResult();
|
||||
// If we didn't find a more immediate 'parent' declaration for the
|
||||
// statement, set the function as the parent.
|
||||
if (Dcl == 0)
|
||||
Dcl = D;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeclLocResolver::VisitVarDecl(VarDecl *D) {
|
||||
CheckRange(D);
|
||||
if (!FoundIt())
|
||||
return;
|
||||
assert(Dcl == D && "Result not updated ?");
|
||||
|
||||
// Check whether the location points to the init expression.
|
||||
if (D->getInit()) {
|
||||
StmtLocResolver SLR(Ctx, Loc);
|
||||
SLR.Visit(D->getInit());
|
||||
Stm = SLR.getStmt();
|
||||
}
|
||||
}
|
||||
|
||||
void DeclLocResolver::VisitDecl(Decl *D) {
|
||||
CheckRange(D);
|
||||
}
|
||||
|
||||
void LocResolverBase::CheckRange(Decl *D) {
|
||||
SourceRange Range = D->getSourceRange();
|
||||
if (!Range.isValid())
|
||||
return;
|
||||
|
||||
FixRange(Range);
|
||||
|
||||
SourceManager &SourceMgr = Ctx.getSourceManager();
|
||||
if (SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), Loc))
|
||||
return;
|
||||
|
||||
if (SourceMgr.isBeforeInTranslationUnit(Loc, Range.getBegin()))
|
||||
PassedLoc = true;
|
||||
else
|
||||
Dcl = D;
|
||||
}
|
||||
|
||||
void LocResolverBase::CheckRange(Stmt *Node) {
|
||||
SourceRange Range = Node->getSourceRange();
|
||||
if (!Range.isValid())
|
||||
return;
|
||||
|
||||
FixRange(Range);
|
||||
|
||||
SourceManager &SourceMgr = Ctx.getSourceManager();
|
||||
if (SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), Loc))
|
||||
return;
|
||||
|
||||
if (SourceMgr.isBeforeInTranslationUnit(Loc, Range.getBegin()))
|
||||
PassedLoc = true;
|
||||
else
|
||||
Stm = Node;
|
||||
}
|
||||
|
||||
void LocResolverBase::FixRange(SourceRange &Range) {
|
||||
if (!Range.isValid())
|
||||
return;
|
||||
|
||||
unsigned TokSize = Lexer::MeasureTokenLength(Range.getEnd(),
|
||||
Ctx.getSourceManager(),
|
||||
Ctx.getLangOptions());
|
||||
Range.setEnd(Range.getEnd().getFileLocWithOffset(TokSize-1));
|
||||
}
|
||||
|
||||
void LocResolverBase::print(Decl *D) {
|
||||
llvm::raw_ostream &OS = llvm::outs();
|
||||
OS << "#### DECL ####\n";
|
||||
D->print(OS);
|
||||
OS << " <";
|
||||
D->getLocStart().print(OS, Ctx.getSourceManager());
|
||||
OS << " > - <";
|
||||
D->getLocEnd().print(OS, Ctx.getSourceManager());
|
||||
OS << ">\n\n";
|
||||
OS.flush();
|
||||
}
|
||||
|
||||
void LocResolverBase::print(Stmt *Node) {
|
||||
llvm::raw_ostream &OS = llvm::outs();
|
||||
OS << "#### STMT ####\n";
|
||||
Node->printPretty(OS, Ctx, 0, PrintingPolicy(Ctx.getLangOptions()));
|
||||
OS << " <";
|
||||
Node->getLocStart().print(OS, Ctx.getSourceManager());
|
||||
OS << " > - <";
|
||||
Node->getLocEnd().print(OS, Ctx.getSourceManager());
|
||||
OS << ">\n\n";
|
||||
OS.flush();
|
||||
}
|
||||
|
||||
|
||||
/// \brief Returns the AST node that a source location points to.
|
||||
///
|
||||
std::pair<Decl *, Stmt *>
|
||||
clang::ResolveLocationInAST(ASTContext &Ctx, SourceLocation Loc) {
|
||||
if (Loc.isInvalid())
|
||||
return std::make_pair((Decl*)0, (Stmt*)0);
|
||||
|
||||
DeclLocResolver DLR(Ctx, Loc);
|
||||
DLR.Visit(Ctx.getTranslationUnitDecl());
|
||||
return DLR.getResult();
|
||||
}
|
@ -1,350 +0,0 @@
|
||||
//===---- SemaInherit.cpp - C++ Inheritance ---------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides Sema routines for C++ inheritance semantics,
|
||||
// including searching the inheritance hierarchy.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "SemaInherit.h"
|
||||
#include "Sema.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/AST/TypeOrdering.h"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
using namespace clang;
|
||||
|
||||
/// \brief Computes the set of declarations referenced by these base
|
||||
/// paths.
|
||||
void BasePaths::ComputeDeclsFound() {
|
||||
assert(NumDeclsFound == 0 && !DeclsFound &&
|
||||
"Already computed the set of declarations");
|
||||
|
||||
std::set<NamedDecl *> Decls;
|
||||
for (BasePaths::paths_iterator Path = begin(), PathEnd = end();
|
||||
Path != PathEnd; ++Path)
|
||||
Decls.insert(*Path->Decls.first);
|
||||
|
||||
NumDeclsFound = Decls.size();
|
||||
DeclsFound = new NamedDecl * [NumDeclsFound];
|
||||
std::copy(Decls.begin(), Decls.end(), DeclsFound);
|
||||
}
|
||||
|
||||
BasePaths::decl_iterator BasePaths::found_decls_begin() {
|
||||
if (NumDeclsFound == 0)
|
||||
ComputeDeclsFound();
|
||||
return DeclsFound;
|
||||
}
|
||||
|
||||
BasePaths::decl_iterator BasePaths::found_decls_end() {
|
||||
if (NumDeclsFound == 0)
|
||||
ComputeDeclsFound();
|
||||
return DeclsFound + NumDeclsFound;
|
||||
}
|
||||
|
||||
/// isAmbiguous - Determines whether the set of paths provided is
|
||||
/// ambiguous, i.e., there are two or more paths that refer to
|
||||
/// different base class subobjects of the same type. BaseType must be
|
||||
/// an unqualified, canonical class type.
|
||||
bool BasePaths::isAmbiguous(QualType BaseType) {
|
||||
assert(BaseType->isCanonical() && "Base type must be the canonical type");
|
||||
assert(BaseType.getCVRQualifiers() == 0 && "Base type must be unqualified");
|
||||
std::pair<bool, unsigned>& Subobjects = ClassSubobjects[BaseType];
|
||||
return Subobjects.second + (Subobjects.first? 1 : 0) > 1;
|
||||
}
|
||||
|
||||
/// clear - Clear out all prior path information.
|
||||
void BasePaths::clear() {
|
||||
Paths.clear();
|
||||
ClassSubobjects.clear();
|
||||
ScratchPath.clear();
|
||||
DetectedVirtual = 0;
|
||||
}
|
||||
|
||||
/// @brief Swaps the contents of this BasePaths structure with the
|
||||
/// contents of Other.
|
||||
void BasePaths::swap(BasePaths &Other) {
|
||||
std::swap(Origin, Other.Origin);
|
||||
Paths.swap(Other.Paths);
|
||||
ClassSubobjects.swap(Other.ClassSubobjects);
|
||||
std::swap(FindAmbiguities, Other.FindAmbiguities);
|
||||
std::swap(RecordPaths, Other.RecordPaths);
|
||||
std::swap(DetectVirtual, Other.DetectVirtual);
|
||||
std::swap(DetectedVirtual, Other.DetectedVirtual);
|
||||
}
|
||||
|
||||
/// IsDerivedFrom - Determine whether the type Derived is derived from
|
||||
/// the type Base, ignoring qualifiers on Base and Derived. This
|
||||
/// routine does not assess whether an actual conversion from a
|
||||
/// Derived* to a Base* is legal, because it does not account for
|
||||
/// ambiguous conversions or conversions to private/protected bases.
|
||||
bool Sema::IsDerivedFrom(QualType Derived, QualType Base) {
|
||||
BasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/false,
|
||||
/*DetectVirtual=*/false);
|
||||
return IsDerivedFrom(Derived, Base, Paths);
|
||||
}
|
||||
|
||||
/// IsDerivedFrom - Determine whether the type Derived is derived from
|
||||
/// the type Base, ignoring qualifiers on Base and Derived. This
|
||||
/// routine does not assess whether an actual conversion from a
|
||||
/// Derived* to a Base* is legal, because it does not account for
|
||||
/// ambiguous conversions or conversions to private/protected
|
||||
/// bases. This routine will use Paths to determine if there are
|
||||
/// ambiguous paths (if @c Paths.isFindingAmbiguities()) and record
|
||||
/// information about all of the paths (if @c Paths.isRecordingPaths()).
|
||||
bool Sema::IsDerivedFrom(QualType Derived, QualType Base, BasePaths &Paths) {
|
||||
Derived = Context.getCanonicalType(Derived).getUnqualifiedType();
|
||||
Base = Context.getCanonicalType(Base).getUnqualifiedType();
|
||||
|
||||
if (!Derived->isRecordType() || !Base->isRecordType())
|
||||
return false;
|
||||
|
||||
if (Derived == Base)
|
||||
return false;
|
||||
|
||||
Paths.setOrigin(Derived);
|
||||
return LookupInBases(cast<CXXRecordDecl>(Derived->getAsRecordType()->getDecl()),
|
||||
MemberLookupCriteria(Base), Paths);
|
||||
}
|
||||
|
||||
/// LookupInBases - Look for something that meets the specified
|
||||
/// Criteria within the base classes of Class (or any of its base
|
||||
/// classes, transitively). This routine populates BasePaths with the
|
||||
/// list of paths that one can take to find the entity that meets the
|
||||
/// search criteria, and returns true if any such entity is found. The
|
||||
/// various options passed to the BasePath constructor will affect the
|
||||
/// behavior of this lookup, e.g., whether it finds ambiguities,
|
||||
/// records paths, or attempts to detect the use of virtual base
|
||||
/// classes.
|
||||
bool Sema::LookupInBases(CXXRecordDecl *Class,
|
||||
const MemberLookupCriteria& Criteria,
|
||||
BasePaths &Paths) {
|
||||
bool FoundPath = false;
|
||||
|
||||
for (CXXRecordDecl::base_class_const_iterator BaseSpec = Class->bases_begin(),
|
||||
BaseSpecEnd = Class->bases_end();
|
||||
BaseSpec != BaseSpecEnd; ++BaseSpec) {
|
||||
// Find the record of the base class subobjects for this type.
|
||||
QualType BaseType = Context.getCanonicalType(BaseSpec->getType());
|
||||
BaseType = BaseType.getUnqualifiedType();
|
||||
|
||||
// If a base class of the class template depends on a template-parameter,
|
||||
// the base class scope is not examined during unqualified name lookup.
|
||||
// [temp.dep]p3.
|
||||
if (BaseType->isDependentType())
|
||||
continue;
|
||||
|
||||
// Determine whether we need to visit this base class at all,
|
||||
// updating the count of subobjects appropriately.
|
||||
std::pair<bool, unsigned>& Subobjects = Paths.ClassSubobjects[BaseType];
|
||||
bool VisitBase = true;
|
||||
bool SetVirtual = false;
|
||||
if (BaseSpec->isVirtual()) {
|
||||
VisitBase = !Subobjects.first;
|
||||
Subobjects.first = true;
|
||||
if (Paths.isDetectingVirtual() && Paths.DetectedVirtual == 0) {
|
||||
// If this is the first virtual we find, remember it. If it turns out
|
||||
// there is no base path here, we'll reset it later.
|
||||
Paths.DetectedVirtual = BaseType->getAsRecordType();
|
||||
SetVirtual = true;
|
||||
}
|
||||
} else
|
||||
++Subobjects.second;
|
||||
|
||||
if (Paths.isRecordingPaths()) {
|
||||
// Add this base specifier to the current path.
|
||||
BasePathElement Element;
|
||||
Element.Base = &*BaseSpec;
|
||||
Element.Class = Class;
|
||||
if (BaseSpec->isVirtual())
|
||||
Element.SubobjectNumber = 0;
|
||||
else
|
||||
Element.SubobjectNumber = Subobjects.second;
|
||||
Paths.ScratchPath.push_back(Element);
|
||||
}
|
||||
|
||||
CXXRecordDecl *BaseRecord
|
||||
= cast<CXXRecordDecl>(BaseSpec->getType()->getAsRecordType()->getDecl());
|
||||
|
||||
// Either look at the base class type or look into the base class
|
||||
// type to see if we've found a member that meets the search
|
||||
// criteria.
|
||||
bool FoundPathToThisBase = false;
|
||||
switch (Criteria.Kind) {
|
||||
case MemberLookupCriteria::LK_Base:
|
||||
FoundPathToThisBase
|
||||
= (Context.getCanonicalType(BaseSpec->getType()) == Criteria.Base);
|
||||
break;
|
||||
case MemberLookupCriteria::LK_NamedMember:
|
||||
Paths.ScratchPath.Decls = BaseRecord->lookup(Criteria.Name);
|
||||
while (Paths.ScratchPath.Decls.first != Paths.ScratchPath.Decls.second) {
|
||||
if (isAcceptableLookupResult(*Paths.ScratchPath.Decls.first,
|
||||
Criteria.NameKind, Criteria.IDNS)) {
|
||||
FoundPathToThisBase = true;
|
||||
break;
|
||||
}
|
||||
++Paths.ScratchPath.Decls.first;
|
||||
}
|
||||
break;
|
||||
case MemberLookupCriteria::LK_OverriddenMember:
|
||||
Paths.ScratchPath.Decls =
|
||||
BaseRecord->lookup(Criteria.Method->getDeclName());
|
||||
while (Paths.ScratchPath.Decls.first != Paths.ScratchPath.Decls.second) {
|
||||
if (CXXMethodDecl *MD =
|
||||
dyn_cast<CXXMethodDecl>(*Paths.ScratchPath.Decls.first)) {
|
||||
OverloadedFunctionDecl::function_iterator MatchedDecl;
|
||||
if (MD->isVirtual() &&
|
||||
!IsOverload(Criteria.Method, MD, MatchedDecl)) {
|
||||
FoundPathToThisBase = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++Paths.ScratchPath.Decls.first;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (FoundPathToThisBase) {
|
||||
// We've found a path that terminates that this base.
|
||||
FoundPath = true;
|
||||
if (Paths.isRecordingPaths()) {
|
||||
// We have a path. Make a copy of it before moving on.
|
||||
Paths.Paths.push_back(Paths.ScratchPath);
|
||||
} else if (!Paths.isFindingAmbiguities()) {
|
||||
// We found a path and we don't care about ambiguities;
|
||||
// return immediately.
|
||||
return FoundPath;
|
||||
}
|
||||
}
|
||||
// C++ [class.member.lookup]p2:
|
||||
// A member name f in one sub-object B hides a member name f in
|
||||
// a sub-object A if A is a base class sub-object of B. Any
|
||||
// declarations that are so hidden are eliminated from
|
||||
// consideration.
|
||||
else if (VisitBase && LookupInBases(BaseRecord, Criteria, Paths)) {
|
||||
// There is a path to a base class that meets the criteria. If we're not
|
||||
// collecting paths or finding ambiguities, we're done.
|
||||
FoundPath = true;
|
||||
if (!Paths.isFindingAmbiguities())
|
||||
return FoundPath;
|
||||
}
|
||||
|
||||
// Pop this base specifier off the current path (if we're
|
||||
// collecting paths).
|
||||
if (Paths.isRecordingPaths())
|
||||
Paths.ScratchPath.pop_back();
|
||||
// If we set a virtual earlier, and this isn't a path, forget it again.
|
||||
if (SetVirtual && !FoundPath) {
|
||||
Paths.DetectedVirtual = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return FoundPath;
|
||||
}
|
||||
|
||||
/// CheckDerivedToBaseConversion - Check whether the Derived-to-Base
|
||||
/// conversion (where Derived and Base are class types) is
|
||||
/// well-formed, meaning that the conversion is unambiguous (and
|
||||
/// that all of the base classes are accessible). Returns true
|
||||
/// and emits a diagnostic if the code is ill-formed, returns false
|
||||
/// otherwise. Loc is the location where this routine should point to
|
||||
/// if there is an error, and Range is the source range to highlight
|
||||
/// if there is an error.
|
||||
bool
|
||||
Sema::CheckDerivedToBaseConversion(QualType Derived, QualType Base,
|
||||
unsigned InaccessibleBaseID,
|
||||
unsigned AmbigiousBaseConvID,
|
||||
SourceLocation Loc, SourceRange Range,
|
||||
DeclarationName Name) {
|
||||
// First, determine whether the path from Derived to Base is
|
||||
// ambiguous. This is slightly more expensive than checking whether
|
||||
// the Derived to Base conversion exists, because here we need to
|
||||
// explore multiple paths to determine if there is an ambiguity.
|
||||
BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
|
||||
/*DetectVirtual=*/false);
|
||||
bool DerivationOkay = IsDerivedFrom(Derived, Base, Paths);
|
||||
assert(DerivationOkay &&
|
||||
"Can only be used with a derived-to-base conversion");
|
||||
(void)DerivationOkay;
|
||||
|
||||
if (!Paths.isAmbiguous(Context.getCanonicalType(Base).getUnqualifiedType())) {
|
||||
// Check that the base class can be accessed.
|
||||
return CheckBaseClassAccess(Derived, Base, InaccessibleBaseID, Paths, Loc,
|
||||
Name);
|
||||
}
|
||||
|
||||
// We know that the derived-to-base conversion is ambiguous, and
|
||||
// we're going to produce a diagnostic. Perform the derived-to-base
|
||||
// search just one more time to compute all of the possible paths so
|
||||
// that we can print them out. This is more expensive than any of
|
||||
// the previous derived-to-base checks we've done, but at this point
|
||||
// performance isn't as much of an issue.
|
||||
Paths.clear();
|
||||
Paths.setRecordingPaths(true);
|
||||
bool StillOkay = IsDerivedFrom(Derived, Base, Paths);
|
||||
assert(StillOkay && "Can only be used with a derived-to-base conversion");
|
||||
(void)StillOkay;
|
||||
|
||||
// Build up a textual representation of the ambiguous paths, e.g.,
|
||||
// D -> B -> A, that will be used to illustrate the ambiguous
|
||||
// conversions in the diagnostic. We only print one of the paths
|
||||
// to each base class subobject.
|
||||
std::string PathDisplayStr = getAmbiguousPathsDisplayString(Paths);
|
||||
|
||||
Diag(Loc, AmbigiousBaseConvID)
|
||||
<< Derived << Base << PathDisplayStr << Range << Name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Sema::CheckDerivedToBaseConversion(QualType Derived, QualType Base,
|
||||
SourceLocation Loc, SourceRange Range) {
|
||||
return CheckDerivedToBaseConversion(Derived, Base,
|
||||
diag::err_conv_to_inaccessible_base,
|
||||
diag::err_ambiguous_derived_to_base_conv,
|
||||
Loc, Range, DeclarationName());
|
||||
}
|
||||
|
||||
|
||||
/// @brief Builds a string representing ambiguous paths from a
|
||||
/// specific derived class to different subobjects of the same base
|
||||
/// class.
|
||||
///
|
||||
/// This function builds a string that can be used in error messages
|
||||
/// to show the different paths that one can take through the
|
||||
/// inheritance hierarchy to go from the derived class to different
|
||||
/// subobjects of a base class. The result looks something like this:
|
||||
/// @code
|
||||
/// struct D -> struct B -> struct A
|
||||
/// struct D -> struct C -> struct A
|
||||
/// @endcode
|
||||
std::string Sema::getAmbiguousPathsDisplayString(BasePaths &Paths) {
|
||||
std::string PathDisplayStr;
|
||||
std::set<unsigned> DisplayedPaths;
|
||||
for (BasePaths::paths_iterator Path = Paths.begin();
|
||||
Path != Paths.end(); ++Path) {
|
||||
if (DisplayedPaths.insert(Path->back().SubobjectNumber).second) {
|
||||
// We haven't displayed a path to this particular base
|
||||
// class subobject yet.
|
||||
PathDisplayStr += "\n ";
|
||||
PathDisplayStr += Paths.getOrigin().getAsString();
|
||||
for (BasePath::const_iterator Element = Path->begin();
|
||||
Element != Path->end(); ++Element)
|
||||
PathDisplayStr += " -> " + Element->Base->getType().getAsString();
|
||||
}
|
||||
}
|
||||
|
||||
return PathDisplayStr;
|
||||
}
|
@ -1,248 +0,0 @@
|
||||
//===------ SemaInherit.h - C++ Inheritance ---------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides Sema data structures that help analyse C++
|
||||
// inheritance semantics, including searching the inheritance
|
||||
// hierarchy.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_SEMA_INHERIT_H
|
||||
#define LLVM_CLANG_SEMA_INHERIT_H
|
||||
|
||||
#include "Sema.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/AST/DeclBase.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/AST/TypeOrdering.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
namespace clang {
|
||||
class CXXBaseSpecifier;
|
||||
|
||||
/// BasePathElement - An element in a path from a derived class to a
|
||||
/// base class. Each step in the path references the link from a
|
||||
/// derived class to one of its direct base classes, along with a
|
||||
/// base "number" that identifies which base subobject of the
|
||||
/// original derived class we are referencing.
|
||||
struct BasePathElement {
|
||||
/// Base - The base specifier that states the link from a derived
|
||||
/// class to a base class, which will be followed by this base
|
||||
/// path element.
|
||||
const CXXBaseSpecifier *Base;
|
||||
|
||||
/// Class - The record decl of the class that the base is a base of.
|
||||
const CXXRecordDecl *Class;
|
||||
|
||||
/// SubobjectNumber - Identifies which base class subobject (of type
|
||||
/// @c Base->getType()) this base path element refers to. This
|
||||
/// value is only valid if @c !Base->isVirtual(), because there
|
||||
/// is no base numbering for the zero or one virtual bases of a
|
||||
/// given type.
|
||||
int SubobjectNumber;
|
||||
};
|
||||
|
||||
/// BasePath - Represents a path from a specific derived class
|
||||
/// (which is not represented as part of the path) to a particular
|
||||
/// (direct or indirect) base class subobject that contains some
|
||||
/// number of declarations with the same name. Individual elements
|
||||
/// in the path are described by the BasePathElement structure,
|
||||
/// which captures both the link from a derived class to one of its
|
||||
/// direct bases and identification describing which base class
|
||||
/// subobject is being used.
|
||||
struct BasePath : public llvm::SmallVector<BasePathElement, 4> {
|
||||
/// Decls - The set of declarations found inside this base class
|
||||
/// subobject.
|
||||
DeclContext::lookup_result Decls;
|
||||
};
|
||||
|
||||
/// BasePaths - Represents the set of paths from a derived class to
|
||||
/// one of its (direct or indirect) bases. For example, given the
|
||||
/// following class hierachy:
|
||||
///
|
||||
/// @code
|
||||
/// class A { };
|
||||
/// class B : public A { };
|
||||
/// class C : public A { };
|
||||
/// class D : public B, public C{ };
|
||||
/// @endcode
|
||||
///
|
||||
/// There are two potential BasePaths to represent paths from D to a
|
||||
/// base subobject of type A. One path is (D,0) -> (B,0) -> (A,0)
|
||||
/// and another is (D,0)->(C,0)->(A,1). These two paths actually
|
||||
/// refer to two different base class subobjects of the same type,
|
||||
/// so the BasePaths object refers to an ambiguous path. On the
|
||||
/// other hand, consider the following class hierarchy:
|
||||
///
|
||||
/// @code
|
||||
/// class A { };
|
||||
/// class B : public virtual A { };
|
||||
/// class C : public virtual A { };
|
||||
/// class D : public B, public C{ };
|
||||
/// @endcode
|
||||
///
|
||||
/// Here, there are two potential BasePaths again, (D, 0) -> (B, 0)
|
||||
/// -> (A,v) and (D, 0) -> (C, 0) -> (A, v), but since both of them
|
||||
/// refer to the same base class subobject of type A (the virtual
|
||||
/// one), there is no ambiguity.
|
||||
class BasePaths {
|
||||
/// Origin - The type from which this search originated.
|
||||
QualType Origin;
|
||||
|
||||
/// Paths - The actual set of paths that can be taken from the
|
||||
/// derived class to the same base class.
|
||||
std::list<BasePath> Paths;
|
||||
|
||||
/// ClassSubobjects - Records the class subobjects for each class
|
||||
/// type that we've seen. The first element in the pair says
|
||||
/// whether we found a path to a virtual base for that class type,
|
||||
/// while the element contains the number of non-virtual base
|
||||
/// class subobjects for that class type. The key of the map is
|
||||
/// the cv-unqualified canonical type of the base class subobject.
|
||||
std::map<QualType, std::pair<bool, unsigned>, QualTypeOrdering>
|
||||
ClassSubobjects;
|
||||
|
||||
/// FindAmbiguities - Whether Sema::IsDerivedFrom should try find
|
||||
/// ambiguous paths while it is looking for a path from a derived
|
||||
/// type to a base type.
|
||||
bool FindAmbiguities;
|
||||
|
||||
/// RecordPaths - Whether Sema::IsDerivedFrom should record paths
|
||||
/// while it is determining whether there are paths from a derived
|
||||
/// type to a base type.
|
||||
bool RecordPaths;
|
||||
|
||||
/// DetectVirtual - Whether Sema::IsDerivedFrom should abort the search
|
||||
/// if it finds a path that goes across a virtual base. The virtual class
|
||||
/// is also recorded.
|
||||
bool DetectVirtual;
|
||||
|
||||
/// ScratchPath - A BasePath that is used by Sema::IsDerivedFrom
|
||||
/// to help build the set of paths.
|
||||
BasePath ScratchPath;
|
||||
|
||||
/// DetectedVirtual - The base class that is virtual.
|
||||
const RecordType *DetectedVirtual;
|
||||
|
||||
/// \brief Array of the declarations that have been found. This
|
||||
/// array is constructed only if needed, e.g., to iterate over the
|
||||
/// results within LookupResult.
|
||||
NamedDecl **DeclsFound;
|
||||
unsigned NumDeclsFound;
|
||||
|
||||
friend class Sema;
|
||||
|
||||
void ComputeDeclsFound();
|
||||
|
||||
public:
|
||||
typedef std::list<BasePath>::const_iterator paths_iterator;
|
||||
typedef NamedDecl **decl_iterator;
|
||||
|
||||
/// BasePaths - Construct a new BasePaths structure to record the
|
||||
/// paths for a derived-to-base search.
|
||||
explicit BasePaths(bool FindAmbiguities = true,
|
||||
bool RecordPaths = true,
|
||||
bool DetectVirtual = true)
|
||||
: FindAmbiguities(FindAmbiguities), RecordPaths(RecordPaths),
|
||||
DetectVirtual(DetectVirtual), DetectedVirtual(0), DeclsFound(0),
|
||||
NumDeclsFound(0)
|
||||
{}
|
||||
|
||||
~BasePaths() { delete [] DeclsFound; }
|
||||
|
||||
paths_iterator begin() const { return Paths.begin(); }
|
||||
paths_iterator end() const { return Paths.end(); }
|
||||
|
||||
BasePath& front() { return Paths.front(); }
|
||||
const BasePath& front() const { return Paths.front(); }
|
||||
|
||||
decl_iterator found_decls_begin();
|
||||
decl_iterator found_decls_end();
|
||||
|
||||
bool isAmbiguous(QualType BaseType);
|
||||
|
||||
/// isFindingAmbiguities - Whether we are finding multiple paths
|
||||
/// to detect ambiguities.
|
||||
bool isFindingAmbiguities() const { return FindAmbiguities; }
|
||||
|
||||
/// isRecordingPaths - Whether we are recording paths.
|
||||
bool isRecordingPaths() const { return RecordPaths; }
|
||||
|
||||
/// setRecordingPaths - Specify whether we should be recording
|
||||
/// paths or not.
|
||||
void setRecordingPaths(bool RP) { RecordPaths = RP; }
|
||||
|
||||
/// isDetectingVirtual - Whether we are detecting virtual bases.
|
||||
bool isDetectingVirtual() const { return DetectVirtual; }
|
||||
|
||||
/// getDetectedVirtual - The virtual base discovered on the path.
|
||||
const RecordType* getDetectedVirtual() const {
|
||||
return DetectedVirtual;
|
||||
}
|
||||
|
||||
/// @brief Retrieve the type from which this base-paths search
|
||||
/// began
|
||||
QualType getOrigin() const { return Origin; }
|
||||
void setOrigin(QualType Type) { Origin = Type; }
|
||||
|
||||
void clear();
|
||||
|
||||
void swap(BasePaths &Other);
|
||||
};
|
||||
|
||||
/// MemberLookupCriteria - Criteria for performing lookup of a
|
||||
/// member of a C++ class. Objects of this type are used to direct
|
||||
/// Sema::LookupCXXClassMember.
|
||||
struct MemberLookupCriteria {
|
||||
/// LookupKind - the kind of lookup we're doing.
|
||||
enum LookupKind {
|
||||
LK_Base,
|
||||
LK_NamedMember,
|
||||
LK_OverriddenMember
|
||||
};
|
||||
|
||||
/// MemberLookupCriteria - Constructs member lookup criteria to
|
||||
/// search for a base class of type Base.
|
||||
explicit MemberLookupCriteria(QualType Base)
|
||||
: Kind(LK_Base), Base(Base) { }
|
||||
|
||||
/// MemberLookupCriteria - Constructs member lookup criteria to
|
||||
/// search for a class member with the given Name.
|
||||
explicit MemberLookupCriteria(DeclarationName Name,
|
||||
Sema::LookupNameKind NameKind,
|
||||
unsigned IDNS)
|
||||
: Kind(LK_NamedMember), Name(Name), NameKind(NameKind), IDNS(IDNS) { }
|
||||
|
||||
explicit MemberLookupCriteria(CXXMethodDecl *MD)
|
||||
: Kind(LK_OverriddenMember), Method(MD) { }
|
||||
|
||||
/// Kind - The kind of lookup we're doing.
|
||||
/// LK_Base if we are looking for a base class (whose
|
||||
/// type is Base). LK_NamedMember if we are looking for a named member of
|
||||
/// the class (with the name Name).
|
||||
LookupKind Kind;
|
||||
|
||||
/// Base - The type of the base class we're searching for, if
|
||||
/// LookupBase is true.
|
||||
QualType Base;
|
||||
|
||||
/// Name - The name of the member we're searching for, if
|
||||
/// LookupBase is false.
|
||||
DeclarationName Name;
|
||||
|
||||
Sema::LookupNameKind NameKind;
|
||||
unsigned IDNS;
|
||||
|
||||
CXXMethodDecl *Method;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -1,932 +0,0 @@
|
||||
//===--- SemaNamedCast.cpp - Semantic Analysis for Named Casts ------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements semantic analysis for C++ named casts.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Sema.h"
|
||||
#include "SemaInherit.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include <set>
|
||||
using namespace clang;
|
||||
|
||||
enum TryStaticCastResult {
|
||||
TSC_NotApplicable, ///< The cast method is not applicable.
|
||||
TSC_Success, ///< The cast method is appropriate and successful.
|
||||
TSC_Failed ///< The cast method is appropriate, but failed. A
|
||||
///< diagnostic has been emitted.
|
||||
};
|
||||
|
||||
static void CheckConstCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
|
||||
const SourceRange &OpRange,
|
||||
const SourceRange &DestRange);
|
||||
static void CheckReinterpretCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
|
||||
const SourceRange &OpRange,
|
||||
const SourceRange &DestRange);
|
||||
static void CheckStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
|
||||
const SourceRange &OpRange);
|
||||
static void CheckDynamicCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
|
||||
const SourceRange &OpRange,
|
||||
const SourceRange &DestRange);
|
||||
|
||||
static bool CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType);
|
||||
static TryStaticCastResult TryLValueToRValueCast(
|
||||
Sema &Self, Expr *SrcExpr, QualType DestType, const SourceRange &OpRange);
|
||||
static TryStaticCastResult TryStaticReferenceDowncast(
|
||||
Sema &Self, Expr *SrcExpr, QualType DestType, const SourceRange &OpRange);
|
||||
static TryStaticCastResult TryStaticPointerDowncast(
|
||||
Sema &Self, QualType SrcType, QualType DestType, const SourceRange &OpRange);
|
||||
static TryStaticCastResult TryStaticMemberPointerUpcast(
|
||||
Sema &Self, QualType SrcType, QualType DestType, const SourceRange &OpRange);
|
||||
static TryStaticCastResult TryStaticDowncast(Sema &Self, QualType SrcType,
|
||||
QualType DestType,
|
||||
const SourceRange &OpRange,
|
||||
QualType OrigSrcType,
|
||||
QualType OrigDestType);
|
||||
static TryStaticCastResult TryStaticImplicitCast(Sema &Self, Expr *SrcExpr,
|
||||
QualType DestType,
|
||||
const SourceRange &OpRange);
|
||||
|
||||
/// ActOnCXXNamedCast - Parse {dynamic,static,reinterpret,const}_cast's.
|
||||
Action::OwningExprResult
|
||||
Sema::ActOnCXXNamedCast(SourceLocation OpLoc, tok::TokenKind Kind,
|
||||
SourceLocation LAngleBracketLoc, TypeTy *Ty,
|
||||
SourceLocation RAngleBracketLoc,
|
||||
SourceLocation LParenLoc, ExprArg E,
|
||||
SourceLocation RParenLoc) {
|
||||
Expr *Ex = E.takeAs<Expr>();
|
||||
QualType DestType = QualType::getFromOpaquePtr(Ty);
|
||||
SourceRange OpRange(OpLoc, RParenLoc);
|
||||
SourceRange DestRange(LAngleBracketLoc, RAngleBracketLoc);
|
||||
|
||||
// If the type is dependent, we won't do the semantic analysis now.
|
||||
// FIXME: should we check this in a more fine-grained manner?
|
||||
bool TypeDependent = DestType->isDependentType() || Ex->isTypeDependent();
|
||||
|
||||
switch (Kind) {
|
||||
default: assert(0 && "Unknown C++ cast!");
|
||||
|
||||
case tok::kw_const_cast:
|
||||
if (!TypeDependent)
|
||||
CheckConstCast(*this, Ex, DestType, OpRange, DestRange);
|
||||
return Owned(new (Context) CXXConstCastExpr(DestType.getNonReferenceType(),
|
||||
Ex, DestType, OpLoc));
|
||||
|
||||
case tok::kw_dynamic_cast:
|
||||
if (!TypeDependent)
|
||||
CheckDynamicCast(*this, Ex, DestType, OpRange, DestRange);
|
||||
return Owned(new (Context)CXXDynamicCastExpr(DestType.getNonReferenceType(),
|
||||
Ex, DestType, OpLoc));
|
||||
|
||||
case tok::kw_reinterpret_cast:
|
||||
if (!TypeDependent)
|
||||
CheckReinterpretCast(*this, Ex, DestType, OpRange, DestRange);
|
||||
return Owned(new (Context) CXXReinterpretCastExpr(
|
||||
DestType.getNonReferenceType(),
|
||||
Ex, DestType, OpLoc));
|
||||
|
||||
case tok::kw_static_cast:
|
||||
if (!TypeDependent)
|
||||
CheckStaticCast(*this, Ex, DestType, OpRange);
|
||||
return Owned(new (Context) CXXStaticCastExpr(DestType.getNonReferenceType(),
|
||||
Ex, DestType, OpLoc));
|
||||
}
|
||||
|
||||
return ExprError();
|
||||
}
|
||||
|
||||
/// CheckConstCast - Check that a const_cast\<DestType\>(SrcExpr) is valid.
|
||||
/// Refer to C++ 5.2.11 for details. const_cast is typically used in code
|
||||
/// like this:
|
||||
/// const char *str = "literal";
|
||||
/// legacy_function(const_cast\<char*\>(str));
|
||||
void
|
||||
CheckConstCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
|
||||
const SourceRange &OpRange, const SourceRange &DestRange)
|
||||
{
|
||||
QualType OrigDestType = DestType, OrigSrcType = SrcExpr->getType();
|
||||
|
||||
DestType = Self.Context.getCanonicalType(DestType);
|
||||
QualType SrcType = SrcExpr->getType();
|
||||
if (const LValueReferenceType *DestTypeTmp =
|
||||
DestType->getAsLValueReferenceType()) {
|
||||
if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid) {
|
||||
// Cannot cast non-lvalue to lvalue reference type.
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_rvalue)
|
||||
<< "const_cast" << OrigDestType << SrcExpr->getSourceRange();
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ 5.2.11p4: An lvalue of type T1 can be [cast] to an lvalue of type T2
|
||||
// [...] if a pointer to T1 can be [cast] to the type pointer to T2.
|
||||
DestType = Self.Context.getPointerType(DestTypeTmp->getPointeeType());
|
||||
SrcType = Self.Context.getPointerType(SrcType);
|
||||
} else {
|
||||
// C++ 5.2.11p1: Otherwise, the result is an rvalue and the
|
||||
// lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard
|
||||
// conversions are performed on the expression.
|
||||
Self.DefaultFunctionArrayConversion(SrcExpr);
|
||||
SrcType = SrcExpr->getType();
|
||||
}
|
||||
|
||||
// C++ 5.2.11p5: For a const_cast involving pointers to data members [...]
|
||||
// the rules for const_cast are the same as those used for pointers.
|
||||
|
||||
if (!DestType->isPointerType() && !DestType->isMemberPointerType()) {
|
||||
// Cannot cast to non-pointer, non-reference type. Note that, if DestType
|
||||
// was a reference type, we converted it to a pointer above.
|
||||
// The status of rvalue references isn't entirely clear, but it looks like
|
||||
// conversion to them is simply invalid.
|
||||
// C++ 5.2.11p3: For two pointer types [...]
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_const_cast_dest)
|
||||
<< OrigDestType << DestRange;
|
||||
return;
|
||||
}
|
||||
if (DestType->isFunctionPointerType() ||
|
||||
DestType->isMemberFunctionPointerType()) {
|
||||
// Cannot cast direct function pointers.
|
||||
// C++ 5.2.11p2: [...] where T is any object type or the void type [...]
|
||||
// T is the ultimate pointee of source and target type.
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_const_cast_dest)
|
||||
<< OrigDestType << DestRange;
|
||||
return;
|
||||
}
|
||||
SrcType = Self.Context.getCanonicalType(SrcType);
|
||||
|
||||
// Unwrap the pointers. Ignore qualifiers. Terminate early if the types are
|
||||
// completely equal.
|
||||
// FIXME: const_cast should probably not be able to convert between pointers
|
||||
// to different address spaces.
|
||||
// C++ 5.2.11p3 describes the core semantics of const_cast. All cv specifiers
|
||||
// in multi-level pointers may change, but the level count must be the same,
|
||||
// as must be the final pointee type.
|
||||
while (SrcType != DestType &&
|
||||
Self.UnwrapSimilarPointerTypes(SrcType, DestType)) {
|
||||
SrcType = SrcType.getUnqualifiedType();
|
||||
DestType = DestType.getUnqualifiedType();
|
||||
}
|
||||
|
||||
// Doug Gregor said to disallow this until users complain.
|
||||
#if 0
|
||||
// If we end up with constant arrays of equal size, unwrap those too. A cast
|
||||
// from const int [N] to int (&)[N] is invalid by my reading of the
|
||||
// standard, but g++ accepts it even with -ansi -pedantic.
|
||||
// No more than one level, though, so don't embed this in the unwrap loop
|
||||
// above.
|
||||
const ConstantArrayType *SrcTypeArr, *DestTypeArr;
|
||||
if ((SrcTypeArr = Self.Context.getAsConstantArrayType(SrcType)) &&
|
||||
(DestTypeArr = Self.Context.getAsConstantArrayType(DestType)))
|
||||
{
|
||||
if (SrcTypeArr->getSize() != DestTypeArr->getSize()) {
|
||||
// Different array sizes.
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
|
||||
<< "const_cast" << OrigDestType << OrigSrcType << OpRange;
|
||||
return;
|
||||
}
|
||||
SrcType = SrcTypeArr->getElementType().getUnqualifiedType();
|
||||
DestType = DestTypeArr->getElementType().getUnqualifiedType();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Since we're dealing in canonical types, the remainder must be the same.
|
||||
if (SrcType != DestType) {
|
||||
// Cast between unrelated types.
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
|
||||
<< "const_cast" << OrigDestType << OrigSrcType << OpRange;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// CheckReinterpretCast - Check that a reinterpret_cast\<DestType\>(SrcExpr) is
|
||||
/// valid.
|
||||
/// Refer to C++ 5.2.10 for details. reinterpret_cast is typically used in code
|
||||
/// like this:
|
||||
/// char *bytes = reinterpret_cast\<char*\>(int_ptr);
|
||||
void
|
||||
CheckReinterpretCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
|
||||
const SourceRange &OpRange, const SourceRange &DestRange)
|
||||
{
|
||||
QualType OrigDestType = DestType, OrigSrcType = SrcExpr->getType();
|
||||
|
||||
DestType = Self.Context.getCanonicalType(DestType);
|
||||
QualType SrcType = SrcExpr->getType();
|
||||
if (const LValueReferenceType *DestTypeTmp =
|
||||
DestType->getAsLValueReferenceType()) {
|
||||
if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid) {
|
||||
// Cannot cast non-lvalue to reference type.
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_rvalue)
|
||||
<< "reinterpret_cast" << OrigDestType << SrcExpr->getSourceRange();
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ 5.2.10p10: [...] a reference cast reinterpret_cast<T&>(x) has the
|
||||
// same effect as the conversion *reinterpret_cast<T*>(&x) with the
|
||||
// built-in & and * operators.
|
||||
// This code does this transformation for the checked types.
|
||||
DestType = Self.Context.getPointerType(DestTypeTmp->getPointeeType());
|
||||
SrcType = Self.Context.getPointerType(SrcType);
|
||||
} else if (const RValueReferenceType *DestTypeTmp =
|
||||
DestType->getAsRValueReferenceType()) {
|
||||
// Both the reference conversion and the rvalue rules apply.
|
||||
Self.DefaultFunctionArrayConversion(SrcExpr);
|
||||
SrcType = SrcExpr->getType();
|
||||
|
||||
DestType = Self.Context.getPointerType(DestTypeTmp->getPointeeType());
|
||||
SrcType = Self.Context.getPointerType(SrcType);
|
||||
} else {
|
||||
// C++ 5.2.10p1: [...] the lvalue-to-rvalue, array-to-pointer, and
|
||||
// function-to-pointer standard conversions are performed on the
|
||||
// expression v.
|
||||
Self.DefaultFunctionArrayConversion(SrcExpr);
|
||||
SrcType = SrcExpr->getType();
|
||||
}
|
||||
|
||||
// Canonicalize source for comparison.
|
||||
SrcType = Self.Context.getCanonicalType(SrcType);
|
||||
|
||||
const MemberPointerType *DestMemPtr = DestType->getAsMemberPointerType(),
|
||||
*SrcMemPtr = SrcType->getAsMemberPointerType();
|
||||
if (DestMemPtr && SrcMemPtr) {
|
||||
// C++ 5.2.10p9: An rvalue of type "pointer to member of X of type T1"
|
||||
// can be explicitly converted to an rvalue of type "pointer to member
|
||||
// of Y of type T2" if T1 and T2 are both function types or both object
|
||||
// types.
|
||||
if (DestMemPtr->getPointeeType()->isFunctionType() !=
|
||||
SrcMemPtr->getPointeeType()->isFunctionType()) {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
|
||||
<< "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ 5.2.10p2: The reinterpret_cast operator shall not cast away
|
||||
// constness.
|
||||
if (CastsAwayConstness(Self, SrcType, DestType)) {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
|
||||
<< "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
|
||||
return;
|
||||
}
|
||||
|
||||
// A valid member pointer cast.
|
||||
return;
|
||||
}
|
||||
|
||||
// See below for the enumeral issue.
|
||||
if (SrcType->isNullPtrType() && DestType->isIntegralType() &&
|
||||
!DestType->isEnumeralType()) {
|
||||
// C++0x 5.2.10p4: A pointer can be explicitly converted to any integral
|
||||
// type large enough to hold it. A value of std::nullptr_t can be
|
||||
// converted to an integral type; the conversion has the same meaning
|
||||
// and validity as a conversion of (void*)0 to the integral type.
|
||||
if (Self.Context.getTypeSize(SrcType) >
|
||||
Self.Context.getTypeSize(DestType)) {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_reinterpret_cast_small_int)
|
||||
<< OrigDestType << DestRange;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool destIsPtr = DestType->isPointerType();
|
||||
bool srcIsPtr = SrcType->isPointerType();
|
||||
if (!destIsPtr && !srcIsPtr) {
|
||||
// Except for std::nullptr_t->integer and lvalue->reference, which are
|
||||
// handled above, at least one of the two arguments must be a pointer.
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
|
||||
<< "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
|
||||
return;
|
||||
}
|
||||
|
||||
if (SrcType == DestType) {
|
||||
// C++ 5.2.10p2 has a note that mentions that, subject to all other
|
||||
// restrictions, a cast to the same type is allowed. The intent is not
|
||||
// entirely clear here, since all other paragraphs explicitly forbid casts
|
||||
// to the same type. However, the behavior of compilers is pretty consistent
|
||||
// on this point: allow same-type conversion if the involved types are
|
||||
// pointers, disallow otherwise.
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: Clang treats enumeration types as integral types. If this is ever
|
||||
// changed for C++, the additional check here will be redundant.
|
||||
if (DestType->isIntegralType() && !DestType->isEnumeralType()) {
|
||||
assert(srcIsPtr && "One type must be a pointer");
|
||||
// C++ 5.2.10p4: A pointer can be explicitly converted to any integral
|
||||
// type large enough to hold it.
|
||||
if (Self.Context.getTypeSize(SrcType) >
|
||||
Self.Context.getTypeSize(DestType)) {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_reinterpret_cast_small_int)
|
||||
<< OrigDestType << DestRange;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (SrcType->isIntegralType() || SrcType->isEnumeralType()) {
|
||||
assert(destIsPtr && "One type must be a pointer");
|
||||
// C++ 5.2.10p5: A value of integral or enumeration type can be explicitly
|
||||
// converted to a pointer.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!destIsPtr || !srcIsPtr) {
|
||||
// With the valid non-pointer conversions out of the way, we can be even
|
||||
// more stringent.
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
|
||||
<< "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ 5.2.10p2: The reinterpret_cast operator shall not cast away constness.
|
||||
if (CastsAwayConstness(Self, SrcType, DestType)) {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
|
||||
<< "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
|
||||
return;
|
||||
}
|
||||
|
||||
// Not casting away constness, so the only remaining check is for compatible
|
||||
// pointer categories.
|
||||
|
||||
if (SrcType->isFunctionPointerType()) {
|
||||
if (DestType->isFunctionPointerType()) {
|
||||
// C++ 5.2.10p6: A pointer to a function can be explicitly converted to
|
||||
// a pointer to a function of a different type.
|
||||
return;
|
||||
}
|
||||
|
||||
// C++0x 5.2.10p8: Converting a pointer to a function into a pointer to
|
||||
// an object type or vice versa is conditionally-supported.
|
||||
// Compilers support it in C++03 too, though, because it's necessary for
|
||||
// casting the return value of dlsym() and GetProcAddress().
|
||||
// FIXME: Conditionally-supported behavior should be configurable in the
|
||||
// TargetInfo or similar.
|
||||
if (!Self.getLangOptions().CPlusPlus0x) {
|
||||
Self.Diag(OpRange.getBegin(), diag::ext_reinterpret_cast_fn_obj)
|
||||
<< OpRange;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DestType->isFunctionPointerType()) {
|
||||
// See above.
|
||||
if (!Self.getLangOptions().CPlusPlus0x) {
|
||||
Self.Diag(OpRange.getBegin(), diag::ext_reinterpret_cast_fn_obj)
|
||||
<< OpRange;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ 5.2.10p7: A pointer to an object can be explicitly converted to
|
||||
// a pointer to an object of different type.
|
||||
// Void pointers are not specified, but supported by every compiler out there.
|
||||
// So we finish by allowing everything that remains - it's got to be two
|
||||
// object pointers.
|
||||
}
|
||||
|
||||
/// CastsAwayConstness - Check if the pointer conversion from SrcType to
|
||||
/// DestType casts away constness as defined in C++ 5.2.11p8ff. This is used by
|
||||
/// the cast checkers. Both arguments must denote pointer (possibly to member)
|
||||
/// types.
|
||||
bool
|
||||
CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType)
|
||||
{
|
||||
// Casting away constness is defined in C++ 5.2.11p8 with reference to
|
||||
// C++ 4.4. We piggyback on Sema::IsQualificationConversion for this, since
|
||||
// the rules are non-trivial. So first we construct Tcv *...cv* as described
|
||||
// in C++ 5.2.11p8.
|
||||
assert((SrcType->isPointerType() || SrcType->isMemberPointerType()) &&
|
||||
"Source type is not pointer or pointer to member.");
|
||||
assert((DestType->isPointerType() || DestType->isMemberPointerType()) &&
|
||||
"Destination type is not pointer or pointer to member.");
|
||||
|
||||
QualType UnwrappedSrcType = SrcType, UnwrappedDestType = DestType;
|
||||
llvm::SmallVector<unsigned, 8> cv1, cv2;
|
||||
|
||||
// Find the qualifications.
|
||||
while (Self.UnwrapSimilarPointerTypes(UnwrappedSrcType, UnwrappedDestType)) {
|
||||
cv1.push_back(UnwrappedSrcType.getCVRQualifiers());
|
||||
cv2.push_back(UnwrappedDestType.getCVRQualifiers());
|
||||
}
|
||||
assert(cv1.size() > 0 && "Must have at least one pointer level.");
|
||||
|
||||
// Construct void pointers with those qualifiers (in reverse order of
|
||||
// unwrapping, of course).
|
||||
QualType SrcConstruct = Self.Context.VoidTy;
|
||||
QualType DestConstruct = Self.Context.VoidTy;
|
||||
for (llvm::SmallVector<unsigned, 8>::reverse_iterator i1 = cv1.rbegin(),
|
||||
i2 = cv2.rbegin();
|
||||
i1 != cv1.rend(); ++i1, ++i2)
|
||||
{
|
||||
SrcConstruct = Self.Context.getPointerType(
|
||||
SrcConstruct.getQualifiedType(*i1));
|
||||
DestConstruct = Self.Context.getPointerType(
|
||||
DestConstruct.getQualifiedType(*i2));
|
||||
}
|
||||
|
||||
// Test if they're compatible.
|
||||
return SrcConstruct != DestConstruct &&
|
||||
!Self.IsQualificationConversion(SrcConstruct, DestConstruct);
|
||||
}
|
||||
|
||||
/// CheckStaticCast - Check that a static_cast\<DestType\>(SrcExpr) is valid.
|
||||
/// Refer to C++ 5.2.9 for details. Static casts are mostly used for making
|
||||
/// implicit conversions explicit and getting rid of data loss warnings.
|
||||
void
|
||||
CheckStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
|
||||
const SourceRange &OpRange)
|
||||
{
|
||||
// The order the tests is not entirely arbitrary. There is one conversion
|
||||
// that can be handled in two different ways. Given:
|
||||
// struct A {};
|
||||
// struct B : public A {
|
||||
// B(); B(const A&);
|
||||
// };
|
||||
// const A &a = B();
|
||||
// the cast static_cast<const B&>(a) could be seen as either a static
|
||||
// reference downcast, or an explicit invocation of the user-defined
|
||||
// conversion using B's conversion constructor.
|
||||
// DR 427 specifies that the downcast is to be applied here.
|
||||
|
||||
// FIXME: With N2812, casts to rvalue refs will change.
|
||||
|
||||
// C++ 5.2.9p4: Any expression can be explicitly converted to type "cv void".
|
||||
if (DestType->isVoidType()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ 5.2.9p5, reference downcast.
|
||||
// See the function for details.
|
||||
// DR 427 specifies that this is to be applied before paragraph 2.
|
||||
if (TryStaticReferenceDowncast(Self, SrcExpr, DestType, OpRange)
|
||||
> TSC_NotApplicable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// N2844 5.2.9p3: An lvalue of type "cv1 T1" can be cast to type "rvalue
|
||||
// reference to cv2 T2" if "cv2 T2" is reference-compatible with "cv1 T1".
|
||||
if (TryLValueToRValueCast(Self, SrcExpr, DestType, OpRange) >
|
||||
TSC_NotApplicable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ 5.2.9p2: An expression e can be explicitly converted to a type T
|
||||
// [...] if the declaration "T t(e);" is well-formed, [...].
|
||||
if (TryStaticImplicitCast(Self, SrcExpr, DestType, OpRange) >
|
||||
TSC_NotApplicable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ 5.2.9p6: May apply the reverse of any standard conversion, except
|
||||
// lvalue-to-rvalue, array-to-pointer, function-to-pointer, and boolean
|
||||
// conversions, subject to further restrictions.
|
||||
// Also, C++ 5.2.9p1 forbids casting away constness, which makes reversal
|
||||
// of qualification conversions impossible.
|
||||
|
||||
// The lvalue-to-rvalue, array-to-pointer and function-to-pointer conversions
|
||||
// are applied to the expression.
|
||||
QualType OrigSrcType = SrcExpr->getType();
|
||||
Self.DefaultFunctionArrayConversion(SrcExpr);
|
||||
|
||||
QualType SrcType = Self.Context.getCanonicalType(SrcExpr->getType());
|
||||
|
||||
// Reverse integral promotion/conversion. All such conversions are themselves
|
||||
// again integral promotions or conversions and are thus already handled by
|
||||
// p2 (TryDirectInitialization above).
|
||||
// (Note: any data loss warnings should be suppressed.)
|
||||
// The exception is the reverse of enum->integer, i.e. integer->enum (and
|
||||
// enum->enum). See also C++ 5.2.9p7.
|
||||
// The same goes for reverse floating point promotion/conversion and
|
||||
// floating-integral conversions. Again, only floating->enum is relevant.
|
||||
if (DestType->isEnumeralType()) {
|
||||
if (SrcType->isComplexType() || SrcType->isVectorType()) {
|
||||
// Fall through - these cannot be converted.
|
||||
} else if (SrcType->isArithmeticType() || SrcType->isEnumeralType()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse pointer upcast. C++ 4.10p3 specifies pointer upcast.
|
||||
// C++ 5.2.9p8 additionally disallows a cast path through virtual inheritance.
|
||||
if (TryStaticPointerDowncast(Self, SrcType, DestType, OpRange)
|
||||
> TSC_NotApplicable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reverse member pointer conversion. C++ 4.11 specifies member pointer
|
||||
// conversion. C++ 5.2.9p9 has additional information.
|
||||
// DR54's access restrictions apply here also.
|
||||
if (TryStaticMemberPointerUpcast(Self, SrcType, DestType, OpRange)
|
||||
> TSC_NotApplicable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reverse pointer conversion to void*. C++ 4.10.p2 specifies conversion to
|
||||
// void*. C++ 5.2.9p10 specifies additional restrictions, which really is
|
||||
// just the usual constness stuff.
|
||||
if (const PointerType *SrcPointer = SrcType->getAsPointerType()) {
|
||||
QualType SrcPointee = SrcPointer->getPointeeType();
|
||||
if (SrcPointee->isVoidType()) {
|
||||
if (const PointerType *DestPointer = DestType->getAsPointerType()) {
|
||||
QualType DestPointee = DestPointer->getPointeeType();
|
||||
if (DestPointee->isIncompleteOrObjectType()) {
|
||||
// This is definitely the intended conversion, but it might fail due
|
||||
// to a const violation.
|
||||
if (!DestPointee.isAtLeastAsQualifiedAs(SrcPointee)) {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
|
||||
<< "static_cast" << DestType << OrigSrcType << OpRange;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We tried everything. Everything! Nothing works! :-(
|
||||
// FIXME: Error reporting could be a lot better. Should store the reason why
|
||||
// every substep failed and, at the end, select the most specific and report
|
||||
// that.
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
|
||||
<< "static_cast" << DestType << OrigSrcType
|
||||
<< OpRange;
|
||||
}
|
||||
|
||||
/// Tests whether a conversion according to N2844 is valid.
|
||||
TryStaticCastResult
|
||||
TryLValueToRValueCast(Sema &Self, Expr *SrcExpr, QualType DestType,
|
||||
const SourceRange &OpRange)
|
||||
{
|
||||
// N2844 5.2.9p3: An lvalue of type "cv1 T1" can be cast to type "rvalue
|
||||
// reference to cv2 T2" if "cv2 T2" is reference-compatible with "cv1 T1".
|
||||
const RValueReferenceType *R = DestType->getAsRValueReferenceType();
|
||||
if (!R)
|
||||
return TSC_NotApplicable;
|
||||
|
||||
if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid)
|
||||
return TSC_NotApplicable;
|
||||
|
||||
// Because we try the reference downcast before this function, from now on
|
||||
// this is the only cast possibility, so we issue an error if we fail now.
|
||||
bool DerivedToBase;
|
||||
if (Self.CompareReferenceRelationship(SrcExpr->getType(), R->getPointeeType(),
|
||||
DerivedToBase) <
|
||||
Sema::Ref_Compatible_With_Added_Qualification) {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_lvalue_to_rvalue_cast)
|
||||
<< SrcExpr->getType() << R->getPointeeType() << OpRange;
|
||||
return TSC_Failed;
|
||||
}
|
||||
|
||||
// FIXME: Similar to CheckReferenceInit, we actually need more AST annotation
|
||||
// than nothing.
|
||||
return TSC_Success;
|
||||
}
|
||||
|
||||
/// Tests whether a conversion according to C++ 5.2.9p5 is valid.
|
||||
TryStaticCastResult
|
||||
TryStaticReferenceDowncast(Sema &Self, Expr *SrcExpr, QualType DestType,
|
||||
const SourceRange &OpRange)
|
||||
{
|
||||
// C++ 5.2.9p5: An lvalue of type "cv1 B", where B is a class type, can be
|
||||
// cast to type "reference to cv2 D", where D is a class derived from B,
|
||||
// if a valid standard conversion from "pointer to D" to "pointer to B"
|
||||
// exists, cv2 >= cv1, and B is not a virtual base class of D.
|
||||
// In addition, DR54 clarifies that the base must be accessible in the
|
||||
// current context. Although the wording of DR54 only applies to the pointer
|
||||
// variant of this rule, the intent is clearly for it to apply to the this
|
||||
// conversion as well.
|
||||
|
||||
if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid) {
|
||||
return TSC_NotApplicable;
|
||||
}
|
||||
|
||||
const ReferenceType *DestReference = DestType->getAsReferenceType();
|
||||
if (!DestReference) {
|
||||
return TSC_NotApplicable;
|
||||
}
|
||||
QualType DestPointee = DestReference->getPointeeType();
|
||||
|
||||
return TryStaticDowncast(Self, SrcExpr->getType(), DestPointee, OpRange,
|
||||
SrcExpr->getType(), DestType);
|
||||
}
|
||||
|
||||
/// Tests whether a conversion according to C++ 5.2.9p8 is valid.
|
||||
TryStaticCastResult
|
||||
TryStaticPointerDowncast(Sema &Self, QualType SrcType, QualType DestType,
|
||||
const SourceRange &OpRange)
|
||||
{
|
||||
// C++ 5.2.9p8: An rvalue of type "pointer to cv1 B", where B is a class
|
||||
// type, can be converted to an rvalue of type "pointer to cv2 D", where D
|
||||
// is a class derived from B, if a valid standard conversion from "pointer
|
||||
// to D" to "pointer to B" exists, cv2 >= cv1, and B is not a virtual base
|
||||
// class of D.
|
||||
// In addition, DR54 clarifies that the base must be accessible in the
|
||||
// current context.
|
||||
|
||||
const PointerType *SrcPointer = SrcType->getAsPointerType();
|
||||
if (!SrcPointer) {
|
||||
return TSC_NotApplicable;
|
||||
}
|
||||
|
||||
const PointerType *DestPointer = DestType->getAsPointerType();
|
||||
if (!DestPointer) {
|
||||
return TSC_NotApplicable;
|
||||
}
|
||||
|
||||
return TryStaticDowncast(Self, SrcPointer->getPointeeType(),
|
||||
DestPointer->getPointeeType(),
|
||||
OpRange, SrcType, DestType);
|
||||
}
|
||||
|
||||
/// TryStaticDowncast - Common functionality of TryStaticReferenceDowncast and
|
||||
/// TryStaticPointerDowncast. Tests whether a static downcast from SrcType to
|
||||
/// DestType, both of which must be canonical, is possible and allowed.
|
||||
TryStaticCastResult
|
||||
TryStaticDowncast(Sema &Self, QualType SrcType, QualType DestType,
|
||||
const SourceRange &OpRange, QualType OrigSrcType,
|
||||
QualType OrigDestType)
|
||||
{
|
||||
// Downcast can only happen in class hierarchies, so we need classes.
|
||||
if (!DestType->isRecordType() || !SrcType->isRecordType()) {
|
||||
return TSC_NotApplicable;
|
||||
}
|
||||
|
||||
BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false,
|
||||
/*DetectVirtual=*/true);
|
||||
if (!Self.IsDerivedFrom(DestType, SrcType, Paths)) {
|
||||
return TSC_NotApplicable;
|
||||
}
|
||||
|
||||
// Target type does derive from source type. Now we're serious. If an error
|
||||
// appears now, it's not ignored.
|
||||
// This may not be entirely in line with the standard. Take for example:
|
||||
// struct A {};
|
||||
// struct B : virtual A {
|
||||
// B(A&);
|
||||
// };
|
||||
//
|
||||
// void f()
|
||||
// {
|
||||
// (void)static_cast<const B&>(*((A*)0));
|
||||
// }
|
||||
// As far as the standard is concerned, p5 does not apply (A is virtual), so
|
||||
// p2 should be used instead - "const B& t(*((A*)0));" is perfectly valid.
|
||||
// However, both GCC and Comeau reject this example, and accepting it would
|
||||
// mean more complex code if we're to preserve the nice error message.
|
||||
// FIXME: Being 100% compliant here would be nice to have.
|
||||
|
||||
// Must preserve cv, as always.
|
||||
if (!DestType.isAtLeastAsQualifiedAs(SrcType)) {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
|
||||
<< "static_cast" << OrigDestType << OrigSrcType << OpRange;
|
||||
return TSC_Failed;
|
||||
}
|
||||
|
||||
if (Paths.isAmbiguous(SrcType.getUnqualifiedType())) {
|
||||
// This code is analoguous to that in CheckDerivedToBaseConversion, except
|
||||
// that it builds the paths in reverse order.
|
||||
// To sum up: record all paths to the base and build a nice string from
|
||||
// them. Use it to spice up the error message.
|
||||
Paths.clear();
|
||||
Paths.setRecordingPaths(true);
|
||||
Self.IsDerivedFrom(DestType, SrcType, Paths);
|
||||
std::string PathDisplayStr;
|
||||
std::set<unsigned> DisplayedPaths;
|
||||
for (BasePaths::paths_iterator Path = Paths.begin();
|
||||
Path != Paths.end(); ++Path) {
|
||||
if (DisplayedPaths.insert(Path->back().SubobjectNumber).second) {
|
||||
// We haven't displayed a path to this particular base
|
||||
// class subobject yet.
|
||||
PathDisplayStr += "\n ";
|
||||
for (BasePath::const_reverse_iterator Element = Path->rbegin();
|
||||
Element != Path->rend(); ++Element)
|
||||
PathDisplayStr += Element->Base->getType().getAsString() + " -> ";
|
||||
PathDisplayStr += DestType.getAsString();
|
||||
}
|
||||
}
|
||||
|
||||
Self.Diag(OpRange.getBegin(), diag::err_ambiguous_base_to_derived_cast)
|
||||
<< SrcType.getUnqualifiedType() << DestType.getUnqualifiedType()
|
||||
<< PathDisplayStr << OpRange;
|
||||
return TSC_Failed;
|
||||
}
|
||||
|
||||
if (Paths.getDetectedVirtual() != 0) {
|
||||
QualType VirtualBase(Paths.getDetectedVirtual(), 0);
|
||||
Self.Diag(OpRange.getBegin(), diag::err_static_downcast_via_virtual)
|
||||
<< OrigSrcType << OrigDestType << VirtualBase << OpRange;
|
||||
return TSC_Failed;
|
||||
}
|
||||
|
||||
// FIXME: Test accessibility.
|
||||
|
||||
return TSC_Success;
|
||||
}
|
||||
|
||||
/// TryStaticMemberPointerUpcast - Tests whether a conversion according to
|
||||
/// C++ 5.2.9p9 is valid:
|
||||
///
|
||||
/// An rvalue of type "pointer to member of D of type cv1 T" can be
|
||||
/// converted to an rvalue of type "pointer to member of B of type cv2 T",
|
||||
/// where B is a base class of D [...].
|
||||
///
|
||||
TryStaticCastResult
|
||||
TryStaticMemberPointerUpcast(Sema &Self, QualType SrcType, QualType DestType,
|
||||
const SourceRange &OpRange)
|
||||
{
|
||||
const MemberPointerType *SrcMemPtr = SrcType->getAsMemberPointerType();
|
||||
if (!SrcMemPtr)
|
||||
return TSC_NotApplicable;
|
||||
const MemberPointerType *DestMemPtr = DestType->getAsMemberPointerType();
|
||||
if (!DestMemPtr)
|
||||
return TSC_NotApplicable;
|
||||
|
||||
// T == T, modulo cv
|
||||
if (Self.Context.getCanonicalType(
|
||||
SrcMemPtr->getPointeeType().getUnqualifiedType()) !=
|
||||
Self.Context.getCanonicalType(DestMemPtr->getPointeeType().
|
||||
getUnqualifiedType()))
|
||||
return TSC_NotApplicable;
|
||||
|
||||
// B base of D
|
||||
QualType SrcClass(SrcMemPtr->getClass(), 0);
|
||||
QualType DestClass(DestMemPtr->getClass(), 0);
|
||||
BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false,
|
||||
/*DetectVirtual=*/true);
|
||||
if (!Self.IsDerivedFrom(SrcClass, DestClass, Paths)) {
|
||||
return TSC_NotApplicable;
|
||||
}
|
||||
|
||||
// B is a base of D. But is it an allowed base? If not, it's a hard error.
|
||||
if (Paths.isAmbiguous(DestClass)) {
|
||||
Paths.clear();
|
||||
Paths.setRecordingPaths(true);
|
||||
bool StillOkay = Self.IsDerivedFrom(SrcClass, DestClass, Paths);
|
||||
assert(StillOkay);
|
||||
StillOkay = StillOkay;
|
||||
std::string PathDisplayStr = Self.getAmbiguousPathsDisplayString(Paths);
|
||||
Self.Diag(OpRange.getBegin(), diag::err_ambiguous_memptr_conv)
|
||||
<< 1 << SrcClass << DestClass << PathDisplayStr << OpRange;
|
||||
return TSC_Failed;
|
||||
}
|
||||
|
||||
if (const RecordType *VBase = Paths.getDetectedVirtual()) {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_memptr_conv_via_virtual)
|
||||
<< SrcClass << DestClass << QualType(VBase, 0) << OpRange;
|
||||
return TSC_Failed;
|
||||
}
|
||||
|
||||
// FIXME: Test accessibility.
|
||||
|
||||
return TSC_Success;
|
||||
}
|
||||
|
||||
/// TryStaticImplicitCast - Tests whether a conversion according to C++ 5.2.9p2
|
||||
/// is valid:
|
||||
///
|
||||
/// An expression e can be explicitly converted to a type T using a
|
||||
/// @c static_cast if the declaration "T t(e);" is well-formed [...].
|
||||
TryStaticCastResult
|
||||
TryStaticImplicitCast(Sema &Self, Expr *SrcExpr, QualType DestType,
|
||||
const SourceRange &OpRange)
|
||||
{
|
||||
if (DestType->isReferenceType()) {
|
||||
// At this point of CheckStaticCast, if the destination is a reference,
|
||||
// this has to work. There is no other way that works.
|
||||
return Self.CheckReferenceInit(SrcExpr, DestType) ?
|
||||
TSC_Failed : TSC_Success;
|
||||
}
|
||||
if (DestType->isRecordType()) {
|
||||
// FIXME: Use an implementation of C++ [over.match.ctor] for this.
|
||||
return TSC_NotApplicable;
|
||||
}
|
||||
|
||||
// FIXME: To get a proper error from invalid conversions here, we need to
|
||||
// reimplement more of this.
|
||||
ImplicitConversionSequence ICS = Self.TryImplicitConversion(
|
||||
SrcExpr, DestType);
|
||||
return ICS.ConversionKind == ImplicitConversionSequence::BadConversion ?
|
||||
TSC_NotApplicable : TSC_Success;
|
||||
}
|
||||
|
||||
/// CheckDynamicCast - Check that a dynamic_cast\<DestType\>(SrcExpr) is valid.
|
||||
/// Refer to C++ 5.2.7 for details. Dynamic casts are used mostly for runtime-
|
||||
/// checked downcasts in class hierarchies.
|
||||
void
|
||||
CheckDynamicCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
|
||||
const SourceRange &OpRange,
|
||||
const SourceRange &DestRange)
|
||||
{
|
||||
QualType OrigDestType = DestType, OrigSrcType = SrcExpr->getType();
|
||||
DestType = Self.Context.getCanonicalType(DestType);
|
||||
|
||||
// C++ 5.2.7p1: T shall be a pointer or reference to a complete class type,
|
||||
// or "pointer to cv void".
|
||||
|
||||
QualType DestPointee;
|
||||
const PointerType *DestPointer = DestType->getAsPointerType();
|
||||
const ReferenceType *DestReference = DestType->getAsReferenceType();
|
||||
if (DestPointer) {
|
||||
DestPointee = DestPointer->getPointeeType();
|
||||
} else if (DestReference) {
|
||||
DestPointee = DestReference->getPointeeType();
|
||||
} else {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_ref_or_ptr)
|
||||
<< OrigDestType << DestRange;
|
||||
return;
|
||||
}
|
||||
|
||||
const RecordType *DestRecord = DestPointee->getAsRecordType();
|
||||
if (DestPointee->isVoidType()) {
|
||||
assert(DestPointer && "Reference to void is not possible");
|
||||
} else if (DestRecord) {
|
||||
if (Self.RequireCompleteType(OpRange.getBegin(), DestPointee,
|
||||
diag::err_bad_dynamic_cast_incomplete,
|
||||
DestRange))
|
||||
return;
|
||||
} else {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class)
|
||||
<< DestPointee.getUnqualifiedType() << DestRange;
|
||||
return;
|
||||
}
|
||||
|
||||
// C++0x 5.2.7p2: If T is a pointer type, v shall be an rvalue of a pointer to
|
||||
// complete class type, [...]. If T is an lvalue reference type, v shall be
|
||||
// an lvalue of a complete class type, [...]. If T is an rvalue reference
|
||||
// type, v shall be an expression having a complete effective class type,
|
||||
// [...]
|
||||
|
||||
QualType SrcType = Self.Context.getCanonicalType(OrigSrcType);
|
||||
QualType SrcPointee;
|
||||
if (DestPointer) {
|
||||
if (const PointerType *SrcPointer = SrcType->getAsPointerType()) {
|
||||
SrcPointee = SrcPointer->getPointeeType();
|
||||
} else {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_ptr)
|
||||
<< OrigSrcType << SrcExpr->getSourceRange();
|
||||
return;
|
||||
}
|
||||
} else if (DestReference->isLValueReferenceType()) {
|
||||
if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid) {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_rvalue)
|
||||
<< "dynamic_cast" << OrigDestType << OpRange;
|
||||
}
|
||||
SrcPointee = SrcType;
|
||||
} else {
|
||||
SrcPointee = SrcType;
|
||||
}
|
||||
|
||||
const RecordType *SrcRecord = SrcPointee->getAsRecordType();
|
||||
if (SrcRecord) {
|
||||
if (Self.RequireCompleteType(OpRange.getBegin(), SrcPointee,
|
||||
diag::err_bad_dynamic_cast_incomplete,
|
||||
SrcExpr->getSourceRange()))
|
||||
return;
|
||||
} else {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class)
|
||||
<< SrcPointee.getUnqualifiedType() << SrcExpr->getSourceRange();
|
||||
return;
|
||||
}
|
||||
|
||||
assert((DestPointer || DestReference) &&
|
||||
"Bad destination non-ptr/ref slipped through.");
|
||||
assert((DestRecord || DestPointee->isVoidType()) &&
|
||||
"Bad destination pointee slipped through.");
|
||||
assert(SrcRecord && "Bad source pointee slipped through.");
|
||||
|
||||
// C++ 5.2.7p1: The dynamic_cast operator shall not cast away constness.
|
||||
if (!DestPointee.isAtLeastAsQualifiedAs(SrcPointee)) {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
|
||||
<< "dynamic_cast" << OrigDestType << OrigSrcType << OpRange;
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ 5.2.7p3: If the type of v is the same as the required result type,
|
||||
// [except for cv].
|
||||
if (DestRecord == SrcRecord) {
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ 5.2.7p5
|
||||
// Upcasts are resolved statically.
|
||||
if (DestRecord && Self.IsDerivedFrom(SrcPointee, DestPointee)) {
|
||||
Self.CheckDerivedToBaseConversion(SrcPointee, DestPointee,
|
||||
OpRange.getBegin(), OpRange);
|
||||
// Diagnostic already emitted on error.
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ 5.2.7p6: Otherwise, v shall be [polymorphic].
|
||||
const RecordDecl *SrcDecl = SrcRecord->getDecl()->getDefinition(Self.Context);
|
||||
assert(SrcDecl && "Definition missing");
|
||||
if (!cast<CXXRecordDecl>(SrcDecl)->isPolymorphic()) {
|
||||
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_polymorphic)
|
||||
<< SrcPointee.getUnqualifiedType() << SrcExpr->getSourceRange();
|
||||
}
|
||||
|
||||
// Done. Everything else is run-time checks.
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,450 +0,0 @@
|
||||
//===--- SemaTemplateInstantiateStmt.cpp - C++ Template Stmt Instantiation ===/
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//===----------------------------------------------------------------------===/
|
||||
//
|
||||
// This file implements C++ template instantiation for statements.
|
||||
//
|
||||
//===----------------------------------------------------------------------===/
|
||||
#include "Sema.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "clang/AST/StmtVisitor.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/Parse/DeclSpec.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
class VISIBILITY_HIDDEN TemplateStmtInstantiator
|
||||
: public StmtVisitor<TemplateStmtInstantiator, Sema::OwningStmtResult> {
|
||||
Sema &SemaRef;
|
||||
const TemplateArgumentList &TemplateArgs;
|
||||
|
||||
template<typename T>
|
||||
Sema::FullExprArg FullExpr(T &expr) {
|
||||
return SemaRef.FullExpr(expr);
|
||||
}
|
||||
|
||||
public:
|
||||
typedef Sema::OwningExprResult OwningExprResult;
|
||||
typedef Sema::OwningStmtResult OwningStmtResult;
|
||||
|
||||
TemplateStmtInstantiator(Sema &SemaRef,
|
||||
const TemplateArgumentList &TemplateArgs)
|
||||
: SemaRef(SemaRef), TemplateArgs(TemplateArgs) { }
|
||||
|
||||
// Declare VisitXXXStmt nodes for all of the statement kinds.
|
||||
#define STMT(Type, Base) OwningStmtResult Visit##Type(Type *S);
|
||||
#define EXPR(Type, Base)
|
||||
#include "clang/AST/StmtNodes.def"
|
||||
|
||||
// Visit an expression (which will use the expression
|
||||
// instantiator).
|
||||
OwningStmtResult VisitExpr(Expr *E);
|
||||
|
||||
// Base case. I'm supposed to ignore this.
|
||||
OwningStmtResult VisitStmt(Stmt *S) {
|
||||
S->dump();
|
||||
assert(false && "Cannot instantiate this kind of statement");
|
||||
return SemaRef.StmtError();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===/
|
||||
// Common/C statements
|
||||
//===----------------------------------------------------------------------===/
|
||||
Sema::OwningStmtResult TemplateStmtInstantiator::VisitDeclStmt(DeclStmt *S) {
|
||||
llvm::SmallVector<Decl *, 4> Decls;
|
||||
for (DeclStmt::decl_iterator D = S->decl_begin(), DEnd = S->decl_end();
|
||||
D != DEnd; ++D) {
|
||||
Decl *Instantiated = SemaRef.InstantiateDecl(*D, SemaRef.CurContext,
|
||||
TemplateArgs);
|
||||
if (!Instantiated)
|
||||
return SemaRef.StmtError();
|
||||
|
||||
Decls.push_back(Instantiated);
|
||||
SemaRef.CurrentInstantiationScope->InstantiatedLocal(*D, Instantiated);
|
||||
}
|
||||
|
||||
return SemaRef.Owned(new (SemaRef.Context) DeclStmt(
|
||||
DeclGroupRef::Create(SemaRef.Context,
|
||||
&Decls[0],
|
||||
Decls.size()),
|
||||
S->getStartLoc(),
|
||||
S->getEndLoc()));
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult TemplateStmtInstantiator::VisitNullStmt(NullStmt *S) {
|
||||
return SemaRef.Owned(S->Clone(SemaRef.Context));
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult TemplateStmtInstantiator::VisitLabelStmt(LabelStmt *S) {
|
||||
OwningStmtResult SubStmt = Visit(S->getSubStmt());
|
||||
|
||||
if (SubStmt.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// FIXME: Pass the real colon loc in.
|
||||
return SemaRef.ActOnLabelStmt(S->getIdentLoc(), S->getID(), SourceLocation(),
|
||||
move(SubStmt));
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult TemplateStmtInstantiator::VisitGotoStmt(GotoStmt *S) {
|
||||
return SemaRef.ActOnGotoStmt(S->getGotoLoc(), S->getLabelLoc(),
|
||||
S->getLabel()->getID());
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitIndirectGotoStmt(IndirectGotoStmt *S) {
|
||||
OwningExprResult Target = SemaRef.InstantiateExpr(S->getTarget(),
|
||||
TemplateArgs);
|
||||
if (Target.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
return SemaRef.ActOnIndirectGotoStmt(S->getGotoLoc(), S->getStarLoc(),
|
||||
move(Target));
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult TemplateStmtInstantiator::VisitBreakStmt(BreakStmt *S) {
|
||||
return SemaRef.Owned(S->Clone(SemaRef.Context));
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitContinueStmt(ContinueStmt *S) {
|
||||
return SemaRef.Owned(S->Clone(SemaRef.Context));
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitReturnStmt(ReturnStmt *S) {
|
||||
Sema::OwningExprResult Result = SemaRef.ExprEmpty();
|
||||
if (Expr *E = S->getRetValue()) {
|
||||
Result = SemaRef.InstantiateExpr(E, TemplateArgs);
|
||||
|
||||
if (Result.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
}
|
||||
|
||||
return SemaRef.ActOnReturnStmt(S->getReturnLoc(), FullExpr(Result));
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitCompoundStmt(CompoundStmt *S) {
|
||||
return SemaRef.InstantiateCompoundStmt(S, TemplateArgs, false);
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitSwitchCase(SwitchCase *S) {
|
||||
assert(false && "SwitchCase statements are never directly instantiated");
|
||||
return SemaRef.StmtError();
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult TemplateStmtInstantiator::VisitCaseStmt(CaseStmt *S) {
|
||||
// The case value expressions are not potentially evaluated.
|
||||
EnterExpressionEvaluationContext Unevaluated(SemaRef, Action::Unevaluated);
|
||||
|
||||
// Instantiate left-hand case value.
|
||||
OwningExprResult LHS = SemaRef.InstantiateExpr(S->getLHS(), TemplateArgs);
|
||||
if (LHS.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// Instantiate right-hand case value (for the GNU case-range extension).
|
||||
OwningExprResult RHS = SemaRef.InstantiateExpr(S->getRHS(), TemplateArgs);
|
||||
if (RHS.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// Build the case statement.
|
||||
OwningStmtResult Case = SemaRef.ActOnCaseStmt(S->getCaseLoc(),
|
||||
move(LHS),
|
||||
S->getEllipsisLoc(),
|
||||
move(RHS),
|
||||
S->getColonLoc());
|
||||
if (Case.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// Instantiate the statement following the case
|
||||
OwningStmtResult SubStmt = SemaRef.InstantiateStmt(S->getSubStmt(),
|
||||
TemplateArgs);
|
||||
if (SubStmt.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
SemaRef.ActOnCaseStmtBody(Case.get(), move(SubStmt));
|
||||
return move(Case);
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitDefaultStmt(DefaultStmt *S) {
|
||||
// Instantiate the statement following the default case
|
||||
OwningStmtResult SubStmt = SemaRef.InstantiateStmt(S->getSubStmt(),
|
||||
TemplateArgs);
|
||||
if (SubStmt.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
return SemaRef.ActOnDefaultStmt(S->getDefaultLoc(),
|
||||
S->getColonLoc(),
|
||||
move(SubStmt),
|
||||
/*CurScope=*/0);
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult TemplateStmtInstantiator::VisitIfStmt(IfStmt *S) {
|
||||
// Instantiate the condition
|
||||
OwningExprResult Cond = SemaRef.InstantiateExpr(S->getCond(), TemplateArgs);
|
||||
if (Cond.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
Sema::FullExprArg FullCond(FullExpr(Cond));
|
||||
|
||||
// Instantiate the "then" branch.
|
||||
OwningStmtResult Then = SemaRef.InstantiateStmt(S->getThen(), TemplateArgs);
|
||||
if (Then.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// Instantiate the "else" branch.
|
||||
OwningStmtResult Else = SemaRef.InstantiateStmt(S->getElse(), TemplateArgs);
|
||||
if (Else.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
return SemaRef.ActOnIfStmt(S->getIfLoc(), FullCond, move(Then),
|
||||
S->getElseLoc(), move(Else));
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitSwitchStmt(SwitchStmt *S) {
|
||||
// Instantiate the condition.
|
||||
OwningExprResult Cond = SemaRef.InstantiateExpr(S->getCond(), TemplateArgs);
|
||||
if (Cond.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// Start the switch statement itself.
|
||||
OwningStmtResult Switch = SemaRef.ActOnStartOfSwitchStmt(move(Cond));
|
||||
if (Switch.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// Instantiate the body of the switch statement.
|
||||
OwningStmtResult Body = SemaRef.InstantiateStmt(S->getBody(), TemplateArgs);
|
||||
if (Body.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// Complete the switch statement.
|
||||
return SemaRef.ActOnFinishSwitchStmt(S->getSwitchLoc(), move(Switch),
|
||||
move(Body));
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult TemplateStmtInstantiator::VisitWhileStmt(WhileStmt *S) {
|
||||
// Instantiate the condition
|
||||
OwningExprResult Cond = SemaRef.InstantiateExpr(S->getCond(), TemplateArgs);
|
||||
if (Cond.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
Sema::FullExprArg FullCond(FullExpr(Cond));
|
||||
|
||||
// Instantiate the body
|
||||
OwningStmtResult Body = SemaRef.InstantiateStmt(S->getBody(), TemplateArgs);
|
||||
if (Body.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
return SemaRef.ActOnWhileStmt(S->getWhileLoc(), FullCond, move(Body));
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult TemplateStmtInstantiator::VisitDoStmt(DoStmt *S) {
|
||||
// Instantiate the condition
|
||||
OwningExprResult Cond = SemaRef.InstantiateExpr(S->getCond(), TemplateArgs);
|
||||
if (Cond.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// Instantiate the body
|
||||
OwningStmtResult Body = SemaRef.InstantiateStmt(S->getBody(), TemplateArgs);
|
||||
if (Body.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
return SemaRef.ActOnDoStmt(S->getDoLoc(), move(Body), S->getWhileLoc(),
|
||||
SourceLocation(), move(Cond), S->getRParenLoc());
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult TemplateStmtInstantiator::VisitForStmt(ForStmt *S) {
|
||||
// Instantiate the initialization statement
|
||||
OwningStmtResult Init = SemaRef.InstantiateStmt(S->getInit(), TemplateArgs);
|
||||
if (Init.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// Instantiate the condition
|
||||
OwningExprResult Cond = SemaRef.InstantiateExpr(S->getCond(), TemplateArgs);
|
||||
if (Cond.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// Instantiate the increment
|
||||
OwningExprResult Inc = SemaRef.InstantiateExpr(S->getInc(), TemplateArgs);
|
||||
if (Inc.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// Instantiate the body
|
||||
OwningStmtResult Body = SemaRef.InstantiateStmt(S->getBody(), TemplateArgs);
|
||||
if (Body.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
return SemaRef.ActOnForStmt(S->getForLoc(), S->getLParenLoc(),
|
||||
move(Init), move(Cond), move(Inc),
|
||||
S->getRParenLoc(), move(Body));
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitAsmStmt(AsmStmt *S) {
|
||||
// FIXME: Implement this
|
||||
assert(false && "Cannot instantiate an 'asm' statement");
|
||||
return SemaRef.StmtError();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===/
|
||||
// C++ statements
|
||||
//===----------------------------------------------------------------------===/
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitCXXTryStmt(CXXTryStmt *S) {
|
||||
// Instantiate the try block itself.
|
||||
OwningStmtResult TryBlock = VisitCompoundStmt(S->getTryBlock());
|
||||
if (TryBlock.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
// Instantiate the handlers.
|
||||
ASTOwningVector<&ActionBase::DeleteStmt> Handlers(SemaRef);
|
||||
for (unsigned I = 0, N = S->getNumHandlers(); I != N; ++I) {
|
||||
OwningStmtResult Handler = VisitCXXCatchStmt(S->getHandler(I));
|
||||
if (Handler.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
Handlers.push_back(Handler.takeAs<Stmt>());
|
||||
}
|
||||
|
||||
return SemaRef.ActOnCXXTryBlock(S->getTryLoc(), move(TryBlock),
|
||||
move_arg(Handlers));
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitCXXCatchStmt(CXXCatchStmt *S) {
|
||||
// Instantiate the exception declaration, if any.
|
||||
VarDecl *Var = 0;
|
||||
if (S->getExceptionDecl()) {
|
||||
VarDecl *ExceptionDecl = S->getExceptionDecl();
|
||||
QualType T = SemaRef.InstantiateType(ExceptionDecl->getType(),
|
||||
TemplateArgs,
|
||||
ExceptionDecl->getLocation(),
|
||||
ExceptionDecl->getDeclName());
|
||||
if (T.isNull())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
Var = SemaRef.BuildExceptionDeclaration(0, T,
|
||||
ExceptionDecl->getIdentifier(),
|
||||
ExceptionDecl->getLocation(),
|
||||
/*FIXME: Inaccurate*/
|
||||
SourceRange(ExceptionDecl->getLocation()));
|
||||
if (Var->isInvalidDecl()) {
|
||||
Var->Destroy(SemaRef.Context);
|
||||
return SemaRef.StmtError();
|
||||
}
|
||||
|
||||
// Introduce the exception declaration into scope.
|
||||
SemaRef.CurrentInstantiationScope->InstantiatedLocal(ExceptionDecl, Var);
|
||||
}
|
||||
|
||||
// Instantiate the actual exception handler.
|
||||
OwningStmtResult Handler = Visit(S->getHandlerBlock());
|
||||
if (Handler.isInvalid()) {
|
||||
if (Var)
|
||||
Var->Destroy(SemaRef.Context);
|
||||
return SemaRef.StmtError();
|
||||
}
|
||||
|
||||
return SemaRef.Owned(new (SemaRef.Context) CXXCatchStmt(S->getCatchLoc(),
|
||||
Var,
|
||||
Handler.takeAs<Stmt>()));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===/
|
||||
// Objective-C statements
|
||||
//===----------------------------------------------------------------------===/
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *S) {
|
||||
// FIXME: Implement this
|
||||
assert(false && "Cannot instantiate an Objective-C @finally statement");
|
||||
return SemaRef.StmtError();
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitObjCAtSynchronizedStmt(
|
||||
ObjCAtSynchronizedStmt *S) {
|
||||
// FIXME: Implement this
|
||||
assert(false && "Cannot instantiate an Objective-C @synchronized statement");
|
||||
return SemaRef.StmtError();
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitObjCAtTryStmt(ObjCAtTryStmt *S) {
|
||||
// FIXME: Implement this
|
||||
assert(false && "Cannot instantiate an Objective-C @try statement");
|
||||
return SemaRef.StmtError();
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitObjCForCollectionStmt(
|
||||
ObjCForCollectionStmt *S) {
|
||||
// FIXME: Implement this
|
||||
assert(false && "Cannot instantiate an Objective-C \"for\" statement");
|
||||
return SemaRef.StmtError();
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) {
|
||||
// FIXME: Implement this
|
||||
assert(false && "Cannot instantiate an Objective-C @throw statement");
|
||||
return SemaRef.StmtError();
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
TemplateStmtInstantiator::VisitObjCAtCatchStmt(ObjCAtCatchStmt *S) {
|
||||
// FIXME: Implement this
|
||||
assert(false && "Cannot instantiate an Objective-C @catch statement");
|
||||
return SemaRef.StmtError();
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult TemplateStmtInstantiator::VisitExpr(Expr *E) {
|
||||
Sema::OwningExprResult Result = SemaRef.InstantiateExpr(E, TemplateArgs);
|
||||
if (Result.isInvalid())
|
||||
return SemaRef.StmtError();
|
||||
|
||||
return SemaRef.Owned(Result.takeAs<Stmt>());
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
Sema::InstantiateStmt(Stmt *S, const TemplateArgumentList &TemplateArgs) {
|
||||
if (!S)
|
||||
return Owned((Stmt *)0);
|
||||
|
||||
TemplateStmtInstantiator Instantiator(*this, TemplateArgs);
|
||||
return Instantiator.Visit(S);
|
||||
}
|
||||
|
||||
Sema::OwningStmtResult
|
||||
Sema::InstantiateCompoundStmt(CompoundStmt *S,
|
||||
const TemplateArgumentList &TemplateArgs,
|
||||
bool isStmtExpr) {
|
||||
if (!S)
|
||||
return Owned((Stmt *)0);
|
||||
|
||||
TemplateStmtInstantiator Instantiator(*this, TemplateArgs);
|
||||
ASTOwningVector<&ActionBase::DeleteStmt> Statements(*this);
|
||||
for (CompoundStmt::body_iterator B = S->body_begin(), BEnd = S->body_end();
|
||||
B != BEnd; ++B) {
|
||||
OwningStmtResult Result = Instantiator.Visit(*B);
|
||||
if (Result.isInvalid())
|
||||
return StmtError();
|
||||
|
||||
Statements.push_back(Result.takeAs<Stmt>());
|
||||
}
|
||||
|
||||
return ActOnCompoundStmt(S->getLBracLoc(), S->getRBracLoc(),
|
||||
move_arg(Statements), isStmtExpr);
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
LEVEL = ../../..
|
||||
include $(LEVEL)/Makefile.common
|
||||
|
||||
# Test in all immediate subdirectories if unset.
|
||||
TESTDIRS ?= $(shell echo $(PROJ_SRC_DIR)/*/)
|
||||
|
||||
ifndef TESTARGS
|
||||
ifdef VERBOSE
|
||||
TESTARGS = -v
|
||||
else
|
||||
TESTARGS = -s
|
||||
endif
|
||||
endif
|
||||
|
||||
all::
|
||||
@ PATH=$(ToolDir):$(LLVM_SRC_ROOT)/test/Scripts:$$PATH VG=$(VG) ../utils/test/MultiTestRunner.py $(TESTARGS) $(TESTDIRS)
|
||||
|
||||
clean::
|
||||
@ rm -rf Output/
|
||||
|
||||
.PHONY: all report clean
|
@ -1,347 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
MultiTestRunner - Harness for running multiple tests in the simple clang style.
|
||||
|
||||
TODO
|
||||
--
|
||||
- Fix Ctrl-c issues
|
||||
- Use a timeout
|
||||
- Detect signalled failures (abort)
|
||||
- Better support for finding tests
|
||||
"""
|
||||
|
||||
# TOD
|
||||
import os, sys, re, random, time
|
||||
import threading
|
||||
import ProgressBar
|
||||
import TestRunner
|
||||
from TestRunner import TestStatus
|
||||
from Queue import Queue
|
||||
|
||||
kTestFileExtensions = set(['.mi','.i','.c','.cpp','.m','.mm','.ll'])
|
||||
|
||||
def getTests(inputs):
|
||||
for path in inputs:
|
||||
if not os.path.exists(path):
|
||||
print >>sys.stderr,"WARNING: Invalid test \"%s\""%(path,)
|
||||
continue
|
||||
|
||||
if os.path.isdir(path):
|
||||
for dirpath,dirnames,filenames in os.walk(path):
|
||||
dotTests = os.path.join(dirpath,'.tests')
|
||||
if os.path.exists(dotTests):
|
||||
for ln in open(dotTests):
|
||||
if ln.strip():
|
||||
yield os.path.join(dirpath,ln.strip())
|
||||
else:
|
||||
# FIXME: This doesn't belong here
|
||||
if 'Output' in dirnames:
|
||||
dirnames.remove('Output')
|
||||
for f in filenames:
|
||||
base,ext = os.path.splitext(f)
|
||||
if ext in kTestFileExtensions:
|
||||
yield os.path.join(dirpath,f)
|
||||
else:
|
||||
yield path
|
||||
|
||||
class TestingProgressDisplay:
|
||||
def __init__(self, opts, numTests, progressBar=None):
|
||||
self.opts = opts
|
||||
self.numTests = numTests
|
||||
self.digits = len(str(self.numTests))
|
||||
self.current = None
|
||||
self.lock = threading.Lock()
|
||||
self.progressBar = progressBar
|
||||
self.progress = 0.
|
||||
|
||||
def update(self, index, tr):
|
||||
# Avoid locking overhead in quiet mode
|
||||
if self.opts.quiet and not tr.failed():
|
||||
return
|
||||
|
||||
# Output lock
|
||||
self.lock.acquire()
|
||||
try:
|
||||
self.handleUpdate(index, tr)
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def finish(self):
|
||||
if self.progressBar:
|
||||
self.progressBar.clear()
|
||||
elif self.opts.succinct:
|
||||
sys.stdout.write('\n')
|
||||
|
||||
def handleUpdate(self, index, tr):
|
||||
if self.progressBar:
|
||||
if tr.failed():
|
||||
self.progressBar.clear()
|
||||
else:
|
||||
# Force monotonicity
|
||||
self.progress = max(self.progress, float(index)/self.numTests)
|
||||
self.progressBar.update(self.progress, tr.path)
|
||||
return
|
||||
elif self.opts.succinct:
|
||||
if not tr.failed():
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
return
|
||||
else:
|
||||
sys.stdout.write('\n')
|
||||
|
||||
extra = ''
|
||||
if tr.code==TestStatus.Invalid:
|
||||
extra = ' - (Invalid test)'
|
||||
elif tr.code==TestStatus.NoRunLine:
|
||||
extra = ' - (No RUN line)'
|
||||
elif tr.failed():
|
||||
extra = ' - %s'%(TestStatus.getName(tr.code).upper(),)
|
||||
print '%*d/%*d - %s%s'%(self.digits, index+1, self.digits,
|
||||
self.numTests, tr.path, extra)
|
||||
|
||||
if tr.failed() and self.opts.showOutput:
|
||||
TestRunner.cat(tr.testResults, sys.stdout)
|
||||
|
||||
class TestResult:
|
||||
def __init__(self, path, code, testResults):
|
||||
self.path = path
|
||||
self.code = code
|
||||
self.testResults = testResults
|
||||
|
||||
def failed(self):
|
||||
return self.code in (TestStatus.Fail,TestStatus.XPass)
|
||||
|
||||
class TestProvider:
|
||||
def __init__(self, opts, tests, display):
|
||||
self.opts = opts
|
||||
self.tests = tests
|
||||
self.index = 0
|
||||
self.lock = threading.Lock()
|
||||
self.results = [None]*len(self.tests)
|
||||
self.startTime = time.time()
|
||||
self.progress = display
|
||||
|
||||
def get(self):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
if self.opts.maxTime is not None:
|
||||
if time.time() - self.startTime > self.opts.maxTime:
|
||||
return None
|
||||
if self.index >= len(self.tests):
|
||||
return None
|
||||
item = self.tests[self.index],self.index
|
||||
self.index += 1
|
||||
return item
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def setResult(self, index, result):
|
||||
self.results[index] = result
|
||||
self.progress.update(index, result)
|
||||
|
||||
class Tester(threading.Thread):
|
||||
def __init__(self, provider):
|
||||
threading.Thread.__init__(self)
|
||||
self.provider = provider
|
||||
|
||||
def run(self):
|
||||
while 1:
|
||||
item = self.provider.get()
|
||||
if item is None:
|
||||
break
|
||||
self.runTest(item)
|
||||
|
||||
def runTest(self, (path,index)):
|
||||
command = path
|
||||
# Use hand concatentation here because we want to override
|
||||
# absolute paths.
|
||||
output = 'Output/' + path + '.out'
|
||||
testname = path
|
||||
testresults = 'Output/' + path + '.testresults'
|
||||
TestRunner.mkdir_p(os.path.dirname(testresults))
|
||||
numTests = len(self.provider.tests)
|
||||
digits = len(str(numTests))
|
||||
code = None
|
||||
try:
|
||||
opts = self.provider.opts
|
||||
if opts.debugDoNotTest:
|
||||
code = None
|
||||
else:
|
||||
code = TestRunner.runOneTest(path, command, output, testname,
|
||||
opts.clang, opts.clangcc,
|
||||
useValgrind=opts.useValgrind,
|
||||
useDGCompat=opts.useDGCompat,
|
||||
useScript=opts.testScript,
|
||||
output=open(testresults,'w'))
|
||||
except KeyboardInterrupt:
|
||||
# This is a sad hack. Unfortunately subprocess goes
|
||||
# bonkers with ctrl-c and we start forking merrily.
|
||||
print 'Ctrl-C detected, goodbye.'
|
||||
os.kill(0,9)
|
||||
|
||||
self.provider.setResult(index, TestResult(path, code, testresults))
|
||||
|
||||
def detectCPUs():
|
||||
"""
|
||||
Detects the number of CPUs on a system. Cribbed from pp.
|
||||
"""
|
||||
# Linux, Unix and MacOS:
|
||||
if hasattr(os, "sysconf"):
|
||||
if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
|
||||
# Linux & Unix:
|
||||
ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
|
||||
if isinstance(ncpus, int) and ncpus > 0:
|
||||
return ncpus
|
||||
else: # OSX:
|
||||
return int(os.popen2("sysctl -n hw.ncpu")[1].read())
|
||||
# Windows:
|
||||
if os.environ.has_key("NUMBER_OF_PROCESSORS"):
|
||||
ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]);
|
||||
if ncpus > 0:
|
||||
return ncpus
|
||||
return 1 # Default
|
||||
|
||||
def main():
|
||||
global options
|
||||
from optparse import OptionParser
|
||||
parser = OptionParser("usage: %prog [options] {inputs}")
|
||||
parser.add_option("-j", "--threads", dest="numThreads",
|
||||
help="Number of testing threads",
|
||||
type=int, action="store",
|
||||
default=detectCPUs())
|
||||
parser.add_option("", "--clang", dest="clang",
|
||||
help="Program to use as \"clang\"",
|
||||
action="store", default=None)
|
||||
parser.add_option("", "--clang-cc", dest="clangcc",
|
||||
help="Program to use as \"clang-cc\"",
|
||||
action="store", default=None)
|
||||
parser.add_option("", "--vg", dest="useValgrind",
|
||||
help="Run tests under valgrind",
|
||||
action="store_true", default=False)
|
||||
parser.add_option("", "--dg", dest="useDGCompat",
|
||||
help="Use llvm dejagnu compatibility mode",
|
||||
action="store_true", default=False)
|
||||
parser.add_option("", "--script", dest="testScript",
|
||||
help="Default script to use",
|
||||
action="store", default=None)
|
||||
parser.add_option("-v", "--verbose", dest="showOutput",
|
||||
help="Show all test output",
|
||||
action="store_true", default=False)
|
||||
parser.add_option("-q", "--quiet", dest="quiet",
|
||||
help="Suppress no error output",
|
||||
action="store_true", default=False)
|
||||
parser.add_option("-s", "--succinct", dest="succinct",
|
||||
help="Reduce amount of output",
|
||||
action="store_true", default=False)
|
||||
parser.add_option("", "--max-tests", dest="maxTests",
|
||||
help="Maximum number of tests to run",
|
||||
action="store", type=int, default=None)
|
||||
parser.add_option("", "--max-time", dest="maxTime",
|
||||
help="Maximum time to spend testing (in seconds)",
|
||||
action="store", type=float, default=None)
|
||||
parser.add_option("", "--shuffle", dest="shuffle",
|
||||
help="Run tests in random order",
|
||||
action="store_true", default=False)
|
||||
parser.add_option("", "--seed", dest="seed",
|
||||
help="Seed for random number generator (default: random)",
|
||||
action="store", default=None)
|
||||
parser.add_option("", "--no-progress-bar", dest="useProgressBar",
|
||||
help="Do not use curses based progress bar",
|
||||
action="store_false", default=True)
|
||||
parser.add_option("", "--debug-do-not-test", dest="debugDoNotTest",
|
||||
help="DEBUG: Skip running actual test script",
|
||||
action="store_true", default=False)
|
||||
parser.add_option("", "--path", dest="path",
|
||||
help="Additional paths to add to testing environment",
|
||||
action="store", type=str, default=None)
|
||||
|
||||
(opts, args) = parser.parse_args()
|
||||
|
||||
if not args:
|
||||
parser.error('No inputs specified')
|
||||
|
||||
if opts.clang is None:
|
||||
opts.clang = TestRunner.inferClang()
|
||||
if opts.clangcc is None:
|
||||
opts.clangcc = TestRunner.inferClangCC(opts.clang)
|
||||
|
||||
# FIXME: It could be worth loading these in parallel with testing.
|
||||
allTests = list(getTests(args))
|
||||
allTests.sort()
|
||||
|
||||
tests = allTests
|
||||
if opts.seed is not None:
|
||||
try:
|
||||
seed = int(opts.seed)
|
||||
except:
|
||||
parser.error('--seed argument should be an integer')
|
||||
random.seed(seed)
|
||||
if opts.shuffle:
|
||||
random.shuffle(tests)
|
||||
if opts.maxTests is not None:
|
||||
tests = tests[:opts.maxTests]
|
||||
if opts.path is not None:
|
||||
os.environ["PATH"] = opts.path + ":" + os.environ["PATH"];
|
||||
|
||||
extra = ''
|
||||
if len(tests) != len(allTests):
|
||||
extra = ' of %d'%(len(allTests),)
|
||||
header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra,
|
||||
opts.numThreads)
|
||||
|
||||
progressBar = None
|
||||
if not opts.quiet:
|
||||
if opts.useProgressBar:
|
||||
try:
|
||||
tc = ProgressBar.TerminalController()
|
||||
progressBar = ProgressBar.ProgressBar(tc, header)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if not progressBar:
|
||||
print header
|
||||
|
||||
display = TestingProgressDisplay(opts, len(tests), progressBar)
|
||||
provider = TestProvider(opts, tests, display)
|
||||
|
||||
testers = [Tester(provider) for i in range(opts.numThreads)]
|
||||
startTime = time.time()
|
||||
for t in testers:
|
||||
t.start()
|
||||
try:
|
||||
for t in testers:
|
||||
t.join()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(1)
|
||||
|
||||
display.finish()
|
||||
|
||||
if not opts.quiet:
|
||||
print 'Testing Time: %.2fs'%(time.time() - startTime)
|
||||
|
||||
# List test results organized organized by kind.
|
||||
byCode = {}
|
||||
for t in provider.results:
|
||||
if t:
|
||||
if t.code not in byCode:
|
||||
byCode[t.code] = []
|
||||
byCode[t.code].append(t)
|
||||
for title,code in (('Expected Failures', TestStatus.XFail),
|
||||
('Unexpected Passing Tests', TestStatus.XPass),
|
||||
('Failing Tests', TestStatus.Fail)):
|
||||
elts = byCode.get(code)
|
||||
if not elts:
|
||||
continue
|
||||
print '*'*20
|
||||
print '%s (%d):' % (title, len(elts))
|
||||
for tr in elts:
|
||||
print '\t%s'%(tr.path,)
|
||||
|
||||
numFailures = len(byCode.get(TestStatus.Fail,[]))
|
||||
if numFailures:
|
||||
print '\nFailures: %d' % (numFailures,)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__=='__main__':
|
||||
main()
|
@ -1,227 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Source: http://code.activestate.com/recipes/475116/, with
|
||||
# modifications by Daniel Dunbar.
|
||||
|
||||
import sys, re, time
|
||||
|
||||
class TerminalController:
|
||||
"""
|
||||
A class that can be used to portably generate formatted output to
|
||||
a terminal.
|
||||
|
||||
`TerminalController` defines a set of instance variables whose
|
||||
values are initialized to the control sequence necessary to
|
||||
perform a given action. These can be simply included in normal
|
||||
output to the terminal:
|
||||
|
||||
>>> term = TerminalController()
|
||||
>>> print 'This is '+term.GREEN+'green'+term.NORMAL
|
||||
|
||||
Alternatively, the `render()` method can used, which replaces
|
||||
'${action}' with the string required to perform 'action':
|
||||
|
||||
>>> term = TerminalController()
|
||||
>>> print term.render('This is ${GREEN}green${NORMAL}')
|
||||
|
||||
If the terminal doesn't support a given action, then the value of
|
||||
the corresponding instance variable will be set to ''. As a
|
||||
result, the above code will still work on terminals that do not
|
||||
support color, except that their output will not be colored.
|
||||
Also, this means that you can test whether the terminal supports a
|
||||
given action by simply testing the truth value of the
|
||||
corresponding instance variable:
|
||||
|
||||
>>> term = TerminalController()
|
||||
>>> if term.CLEAR_SCREEN:
|
||||
... print 'This terminal supports clearning the screen.'
|
||||
|
||||
Finally, if the width and height of the terminal are known, then
|
||||
they will be stored in the `COLS` and `LINES` attributes.
|
||||
"""
|
||||
# Cursor movement:
|
||||
BOL = '' #: Move the cursor to the beginning of the line
|
||||
UP = '' #: Move the cursor up one line
|
||||
DOWN = '' #: Move the cursor down one line
|
||||
LEFT = '' #: Move the cursor left one char
|
||||
RIGHT = '' #: Move the cursor right one char
|
||||
|
||||
# Deletion:
|
||||
CLEAR_SCREEN = '' #: Clear the screen and move to home position
|
||||
CLEAR_EOL = '' #: Clear to the end of the line.
|
||||
CLEAR_BOL = '' #: Clear to the beginning of the line.
|
||||
CLEAR_EOS = '' #: Clear to the end of the screen
|
||||
|
||||
# Output modes:
|
||||
BOLD = '' #: Turn on bold mode
|
||||
BLINK = '' #: Turn on blink mode
|
||||
DIM = '' #: Turn on half-bright mode
|
||||
REVERSE = '' #: Turn on reverse-video mode
|
||||
NORMAL = '' #: Turn off all modes
|
||||
|
||||
# Cursor display:
|
||||
HIDE_CURSOR = '' #: Make the cursor invisible
|
||||
SHOW_CURSOR = '' #: Make the cursor visible
|
||||
|
||||
# Terminal size:
|
||||
COLS = None #: Width of the terminal (None for unknown)
|
||||
LINES = None #: Height of the terminal (None for unknown)
|
||||
|
||||
# Foreground colors:
|
||||
BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
|
||||
|
||||
# Background colors:
|
||||
BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
|
||||
BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
|
||||
|
||||
_STRING_CAPABILITIES = """
|
||||
BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
|
||||
CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
|
||||
BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
|
||||
HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
|
||||
_COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
|
||||
_ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
|
||||
|
||||
def __init__(self, term_stream=sys.stdout):
|
||||
"""
|
||||
Create a `TerminalController` and initialize its attributes
|
||||
with appropriate values for the current terminal.
|
||||
`term_stream` is the stream that will be used for terminal
|
||||
output; if this stream is not a tty, then the terminal is
|
||||
assumed to be a dumb terminal (i.e., have no capabilities).
|
||||
"""
|
||||
# Curses isn't available on all platforms
|
||||
try: import curses
|
||||
except: return
|
||||
|
||||
# If the stream isn't a tty, then assume it has no capabilities.
|
||||
if not term_stream.isatty(): return
|
||||
|
||||
# Check the terminal type. If we fail, then assume that the
|
||||
# terminal has no capabilities.
|
||||
try: curses.setupterm()
|
||||
except: return
|
||||
|
||||
# Look up numeric capabilities.
|
||||
self.COLS = curses.tigetnum('cols')
|
||||
self.LINES = curses.tigetnum('lines')
|
||||
|
||||
# Look up string capabilities.
|
||||
for capability in self._STRING_CAPABILITIES:
|
||||
(attrib, cap_name) = capability.split('=')
|
||||
setattr(self, attrib, self._tigetstr(cap_name) or '')
|
||||
|
||||
# Colors
|
||||
set_fg = self._tigetstr('setf')
|
||||
if set_fg:
|
||||
for i,color in zip(range(len(self._COLORS)), self._COLORS):
|
||||
setattr(self, color, curses.tparm(set_fg, i) or '')
|
||||
set_fg_ansi = self._tigetstr('setaf')
|
||||
if set_fg_ansi:
|
||||
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
|
||||
setattr(self, color, curses.tparm(set_fg_ansi, i) or '')
|
||||
set_bg = self._tigetstr('setb')
|
||||
if set_bg:
|
||||
for i,color in zip(range(len(self._COLORS)), self._COLORS):
|
||||
setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '')
|
||||
set_bg_ansi = self._tigetstr('setab')
|
||||
if set_bg_ansi:
|
||||
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
|
||||
setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')
|
||||
|
||||
def _tigetstr(self, cap_name):
|
||||
# String capabilities can include "delays" of the form "$<2>".
|
||||
# For any modern terminal, we should be able to just ignore
|
||||
# these, so strip them out.
|
||||
import curses
|
||||
cap = curses.tigetstr(cap_name) or ''
|
||||
return re.sub(r'\$<\d+>[/*]?', '', cap)
|
||||
|
||||
def render(self, template):
|
||||
"""
|
||||
Replace each $-substitutions in the given template string with
|
||||
the corresponding terminal control string (if it's defined) or
|
||||
'' (if it's not).
|
||||
"""
|
||||
return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
|
||||
|
||||
def _render_sub(self, match):
|
||||
s = match.group()
|
||||
if s == '$$': return s
|
||||
else: return getattr(self, s[2:-1])
|
||||
|
||||
#######################################################################
|
||||
# Example use case: progress bar
|
||||
#######################################################################
|
||||
|
||||
class ProgressBar:
|
||||
"""
|
||||
A 3-line progress bar, which looks like::
|
||||
|
||||
Header
|
||||
20% [===========----------------------------------]
|
||||
progress message
|
||||
|
||||
The progress bar is colored, if the terminal supports color
|
||||
output; and adjusts to the width of the terminal.
|
||||
"""
|
||||
BAR = '%s${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}%s\n'
|
||||
HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'
|
||||
|
||||
def __init__(self, term, header, useETA=True):
|
||||
self.term = term
|
||||
if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
|
||||
raise ValueError("Terminal isn't capable enough -- you "
|
||||
"should use a simpler progress dispaly.")
|
||||
self.width = self.term.COLS or 75
|
||||
self.bar = term.render(self.BAR)
|
||||
self.header = self.term.render(self.HEADER % header.center(self.width))
|
||||
self.cleared = 1 #: true if we haven't drawn the bar yet.
|
||||
self.useETA = useETA
|
||||
if self.useETA:
|
||||
self.startTime = time.time()
|
||||
self.update(0, '')
|
||||
|
||||
def update(self, percent, message):
|
||||
if self.cleared:
|
||||
sys.stdout.write(self.header)
|
||||
self.cleared = 0
|
||||
prefix = '%3d%% ' % (percent*100,)
|
||||
suffix = ''
|
||||
if self.useETA:
|
||||
elapsed = time.time() - self.startTime
|
||||
if percent > .0001 and elapsed > 1:
|
||||
total = elapsed / percent
|
||||
eta = int(total - elapsed)
|
||||
h = eta//3600.
|
||||
m = (eta//60) % 60
|
||||
s = eta % 60
|
||||
suffix = ' ETA: %02d:%02d:%02d'%(h,m,s)
|
||||
barWidth = self.width - len(prefix) - len(suffix) - 2
|
||||
n = int(barWidth*percent)
|
||||
if len(message) < self.width:
|
||||
message = message + ' '*(self.width - len(message))
|
||||
else:
|
||||
message = '... ' + message[-(self.width-4):]
|
||||
sys.stdout.write(
|
||||
self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
|
||||
(self.bar % (prefix, '='*n, '-'*(barWidth-n), suffix)) +
|
||||
self.term.CLEAR_EOL + message)
|
||||
|
||||
def clear(self):
|
||||
if not self.cleared:
|
||||
sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
|
||||
self.term.UP + self.term.CLEAR_EOL +
|
||||
self.term.UP + self.term.CLEAR_EOL)
|
||||
self.cleared = 1
|
||||
|
||||
def test():
|
||||
import time
|
||||
tc = TerminalController()
|
||||
p = ProgressBar(tc, 'Tests')
|
||||
for i in range(101):
|
||||
p.update(i/100., str(i))
|
||||
time.sleep(.3)
|
||||
|
||||
if __name__=='__main__':
|
||||
test()
|
@ -1,296 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# TestRunner.py - This script is used to run arbitrary unit tests. Unit
|
||||
# tests must contain the command used to run them in the input file, starting
|
||||
# immediately after a "RUN:" string.
|
||||
#
|
||||
# This runner recognizes and replaces the following strings in the command:
|
||||
#
|
||||
# %s - Replaced with the input name of the program, or the program to
|
||||
# execute, as appropriate.
|
||||
# %S - Replaced with the directory where the input resides.
|
||||
# %llvmgcc - llvm-gcc command
|
||||
# %llvmgxx - llvm-g++ command
|
||||
# %prcontext - prcontext.tcl script
|
||||
# %t - temporary file name (derived from testcase name)
|
||||
#
|
||||
|
||||
import errno
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# Increase determinism for things that use the terminal width.
|
||||
#
|
||||
# FIXME: Find a better place for this hack.
|
||||
os.environ['COLUMNS'] = '0'
|
||||
|
||||
class TestStatus:
|
||||
Pass = 0
|
||||
XFail = 1
|
||||
Fail = 2
|
||||
XPass = 3
|
||||
NoRunLine = 4
|
||||
Invalid = 5
|
||||
|
||||
kNames = ['Pass','XFail','Fail','XPass','NoRunLine','Invalid']
|
||||
@staticmethod
|
||||
def getName(code):
|
||||
return TestStatus.kNames[code]
|
||||
|
||||
def mkdir_p(path):
|
||||
if not path:
|
||||
pass
|
||||
elif os.path.exists(path):
|
||||
pass
|
||||
else:
|
||||
parent = os.path.dirname(path)
|
||||
if parent != path:
|
||||
mkdir_p(parent)
|
||||
try:
|
||||
os.mkdir(path)
|
||||
except OSError,e:
|
||||
if e.errno != errno.EEXIST:
|
||||
raise
|
||||
|
||||
def remove(path):
|
||||
try:
|
||||
os.remove(path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def cat(path, output):
|
||||
f = open(path)
|
||||
output.writelines(f)
|
||||
f.close()
|
||||
|
||||
def runOneTest(FILENAME, SUBST, OUTPUT, TESTNAME, CLANG, CLANGCC,
|
||||
useValgrind=False,
|
||||
useDGCompat=False,
|
||||
useScript=None,
|
||||
output=sys.stdout):
|
||||
if useValgrind:
|
||||
VG_OUTPUT = '%s.vg'%(OUTPUT,)
|
||||
if os.path.exists:
|
||||
remove(VG_OUTPUT)
|
||||
CLANG = 'valgrind --leak-check=full --quiet --log-file=%s %s'%(VG_OUTPUT, CLANG)
|
||||
|
||||
# Create the output directory if it does not already exist.
|
||||
mkdir_p(os.path.dirname(OUTPUT))
|
||||
|
||||
# FIXME
|
||||
#ulimit -t 40
|
||||
|
||||
# FIXME: Load script once
|
||||
# FIXME: Support "short" script syntax
|
||||
|
||||
if useScript:
|
||||
scriptFile = useScript
|
||||
else:
|
||||
# See if we have a per-dir test script.
|
||||
dirScriptFile = os.path.join(os.path.dirname(FILENAME), 'test.script')
|
||||
if os.path.exists(dirScriptFile):
|
||||
scriptFile = dirScriptFile
|
||||
else:
|
||||
scriptFile = FILENAME
|
||||
|
||||
# Verify the script contains a run line.
|
||||
for ln in open(scriptFile):
|
||||
if 'RUN:' in ln:
|
||||
break
|
||||
else:
|
||||
print >>output, "******************** TEST '%s' HAS NO RUN LINE! ********************"%(TESTNAME,)
|
||||
output.flush()
|
||||
return TestStatus.NoRunLine
|
||||
|
||||
OUTPUT = os.path.abspath(OUTPUT)
|
||||
FILENAME = os.path.abspath(FILENAME)
|
||||
SCRIPT = OUTPUT + '.script'
|
||||
TEMPOUTPUT = OUTPUT + '.tmp'
|
||||
|
||||
substitutions = [('%s',SUBST),
|
||||
('%S',os.path.dirname(SUBST)),
|
||||
('%llvmgcc','llvm-gcc -emit-llvm -w'),
|
||||
('%llvmgxx','llvm-g++ -emit-llvm -w'),
|
||||
('%prcontext','prcontext.tcl'),
|
||||
('%t',TEMPOUTPUT),
|
||||
(' clang ', ' ' + CLANG + ' '),
|
||||
(' clang-cc ', ' ' + CLANGCC + ' ')]
|
||||
scriptLines = []
|
||||
xfailLines = []
|
||||
for ln in open(scriptFile):
|
||||
if 'RUN:' in ln:
|
||||
# Isolate run parameters
|
||||
index = ln.index('RUN:')
|
||||
ln = ln[index+4:]
|
||||
|
||||
# Apply substitutions
|
||||
for a,b in substitutions:
|
||||
ln = ln.replace(a,b)
|
||||
|
||||
if useDGCompat:
|
||||
ln = re.sub(r'\{(.*)\}', r'"\1"', ln)
|
||||
scriptLines.append(ln)
|
||||
elif 'XFAIL' in ln:
|
||||
xfailLines.append(ln)
|
||||
|
||||
if xfailLines:
|
||||
print >>output, "XFAILED '%s':"%(TESTNAME,)
|
||||
output.writelines(xfailLines)
|
||||
|
||||
# Write script file
|
||||
f = open(SCRIPT,'w')
|
||||
f.write(''.join(scriptLines))
|
||||
f.close()
|
||||
|
||||
outputFile = open(OUTPUT,'w')
|
||||
p = None
|
||||
try:
|
||||
p = subprocess.Popen(["/bin/sh",SCRIPT],
|
||||
cwd=os.path.dirname(FILENAME),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
out,err = p.communicate()
|
||||
outputFile.write(out)
|
||||
outputFile.write(err)
|
||||
SCRIPT_STATUS = p.wait()
|
||||
|
||||
# Detect Ctrl-C in subprocess.
|
||||
if SCRIPT_STATUS == -signal.SIGINT:
|
||||
raise KeyboardInterrupt
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
outputFile.close()
|
||||
|
||||
if xfailLines:
|
||||
SCRIPT_STATUS = not SCRIPT_STATUS
|
||||
|
||||
if useValgrind:
|
||||
VG_STATUS = len(list(open(VG_OUTPUT)))
|
||||
else:
|
||||
VG_STATUS = 0
|
||||
|
||||
if SCRIPT_STATUS or VG_STATUS:
|
||||
print >>output, "******************** TEST '%s' FAILED! ********************"%(TESTNAME,)
|
||||
print >>output, "Command: "
|
||||
output.writelines(scriptLines)
|
||||
if not SCRIPT_STATUS:
|
||||
print >>output, "Output:"
|
||||
else:
|
||||
print >>output, "Incorrect Output:"
|
||||
cat(OUTPUT, output)
|
||||
if VG_STATUS:
|
||||
print >>output, "Valgrind Output:"
|
||||
cat(VG_OUTPUT, output)
|
||||
print >>output, "******************** TEST '%s' FAILED! ********************"%(TESTNAME,)
|
||||
output.flush()
|
||||
if xfailLines:
|
||||
return TestStatus.XPass
|
||||
else:
|
||||
return TestStatus.Fail
|
||||
|
||||
if xfailLines:
|
||||
return TestStatus.XFail
|
||||
else:
|
||||
return TestStatus.Pass
|
||||
|
||||
def capture(args):
|
||||
p = subprocess.Popen(args, stdout=subprocess.PIPE)
|
||||
out,_ = p.communicate()
|
||||
return out
|
||||
|
||||
def which(command):
|
||||
# Would be nice if Python had a lib function for this.
|
||||
res = capture(['which',command])
|
||||
res = res.strip()
|
||||
if res and os.path.exists(res):
|
||||
return res
|
||||
return None
|
||||
|
||||
def inferClang():
|
||||
# Determine which clang to use.
|
||||
clang = os.getenv('CLANG')
|
||||
|
||||
# If the user set clang in the environment, definitely use that and don't
|
||||
# try to validate.
|
||||
if clang:
|
||||
return clang
|
||||
|
||||
# Otherwise look in the path.
|
||||
clang = which('clang')
|
||||
|
||||
if not clang:
|
||||
print >>sys.stderr, "error: couldn't find 'clang' program, try setting CLANG in your environment"
|
||||
sys.exit(1)
|
||||
|
||||
return clang
|
||||
|
||||
def inferClangCC(clang):
|
||||
clangcc = os.getenv('CLANGCC')
|
||||
|
||||
# If the user set clang in the environment, definitely use that and don't
|
||||
# try to validate.
|
||||
if clangcc:
|
||||
return clangcc
|
||||
|
||||
# Otherwise try adding -cc since we expect to be looking in a build
|
||||
# directory.
|
||||
clangcc = which(clang + '-cc')
|
||||
if not clangcc:
|
||||
# Otherwise ask clang.
|
||||
res = capture([clang, '-print-prog-name=clang-cc'])
|
||||
res = res.strip()
|
||||
if res and os.path.exists(res):
|
||||
clangcc = res
|
||||
|
||||
if not clangcc:
|
||||
print >>sys.stderr, "error: couldn't find 'clang-cc' program, try setting CLANGCC in your environment"
|
||||
sys.exit(1)
|
||||
|
||||
return clangcc
|
||||
|
||||
def main():
|
||||
global options
|
||||
from optparse import OptionParser
|
||||
parser = OptionParser("usage: %prog [options] {tests}")
|
||||
parser.add_option("", "--clang", dest="clang",
|
||||
help="Program to use as \"clang\"",
|
||||
action="store", default=None)
|
||||
parser.add_option("", "--clang-cc", dest="clangcc",
|
||||
help="Program to use as \"clang-cc\"",
|
||||
action="store", default=None)
|
||||
parser.add_option("", "--vg", dest="useValgrind",
|
||||
help="Run tests under valgrind",
|
||||
action="store_true", default=False)
|
||||
parser.add_option("", "--dg", dest="useDGCompat",
|
||||
help="Use llvm dejagnu compatibility mode",
|
||||
action="store_true", default=False)
|
||||
(opts, args) = parser.parse_args()
|
||||
|
||||
if not args:
|
||||
parser.error('No tests specified')
|
||||
|
||||
if opts.clang is None:
|
||||
opts.clang = inferClang()
|
||||
if opts.clangcc is None:
|
||||
opts.clangcc = inferClangCC(opts.clang)
|
||||
|
||||
for path in args:
|
||||
command = path
|
||||
# Use hand concatentation here because we want to override
|
||||
# absolute paths.
|
||||
output = 'Output/' + path + '.out'
|
||||
testname = path
|
||||
|
||||
res = runOneTest(path, command, output, testname,
|
||||
opts.clang, opts.clangcc,
|
||||
useValgrind=opts.useValgrind,
|
||||
useDGCompat=opts.useDGCompat,
|
||||
useScript=os.getenv("TEST_SCRIPT"))
|
||||
|
||||
sys.exit(res == TestStatus.Fail or res == TestStatus.XPass)
|
||||
|
||||
if __name__=='__main__':
|
||||
main()
|
Loading…
Reference in New Issue
Block a user