Vendor import of clang trunk r304222:

https://llvm.org/svn/llvm-project/cfe/trunk@304222
This commit is contained in:
Dimitry Andric 2017-05-30 17:37:44 +00:00
parent b5aee35cc5
commit 550ae89a71
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/vendor/clang/dist/; revision=319233
svn path=/vendor/clang/clang-trunk-r304222/; revision=319234; tag=vendor/clang/clang-trunk-r304222
64 changed files with 2789 additions and 2010 deletions

View File

@ -32,7 +32,7 @@
* compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
*/
#define CINDEX_VERSION_MAJOR 0
#define CINDEX_VERSION_MINOR 40
#define CINDEX_VERSION_MINOR 41
#define CINDEX_VERSION_ENCODE(major, minor) ( \
((major) * 10000) \
@ -1418,6 +1418,15 @@ CINDEX_LINKAGE int clang_saveTranslationUnit(CXTranslationUnit TU,
const char *FileName,
unsigned options);
/**
* \brief Suspend a translation unit in order to free memory associated with it.
*
* A suspended translation unit uses significantly less memory but on the other
* side does not support any other calls than \c clang_reparseTranslationUnit
* to resume it or \c clang_disposeTranslationUnit to dispose it completely.
*/
CINDEX_LINKAGE unsigned clang_suspendTranslationUnit(CXTranslationUnit);
/**
* \brief Destroy the specified CXTranslationUnit object.
*/

View File

@ -525,6 +525,8 @@ def err_pp_module_begin_without_module_end : Error<
def err_pp_module_end_without_module_begin : Error<
"no matching '#pragma clang module begin' for this "
"'#pragma clang module end'">;
def note_pp_module_begin_here : Note<
"entering module '%0' due to this pragma">;
def err_defined_macro_name : Error<"'defined' cannot be used as a macro name">;
def err_paste_at_start : Error<

View File

@ -8312,8 +8312,13 @@ def err_opencl_bitfields : Error<
"bit-fields are not supported in OpenCL">;
def err_opencl_vla : Error<
"variable length arrays are not supported in OpenCL">;
def err_opencl_scalar_type_rank_greater_than_vector_type : Error<
"scalar operand type has greater rank than the type of the vector "
"element. (%0 and %1)">;
def err_bad_kernel_param_type : Error<
"%0 cannot be used as the type of a kernel parameter">;
def err_opencl_implicit_function_decl : Error<
"implicit declaration of function %0 is invalid in OpenCL">;
def err_record_with_pointers_kernel_param : Error<
"%select{struct|union}0 kernel parameters may not contain pointers">;
def note_within_field_of_type : Note<
@ -8744,8 +8749,8 @@ def err_omp_not_mappable_type : Error<
"type %0 is not mappable to target">;
def err_omp_invalid_map_type_for_directive : Error<
"%select{map type '%1' is not allowed|map type must be specified}0 for '#pragma omp %2'">;
def err_omp_no_map_for_directive : Error<
"expected at least one map clause for '#pragma omp %0'">;
def err_omp_no_clause_for_directive : Error<
"expected at least one %0 clause for '#pragma omp %1'">;
def note_omp_polymorphic_in_target : Note<
"mappable type cannot be polymorphic">;
def note_omp_static_member_in_target : Note<

View File

@ -878,6 +878,11 @@ class ASTUnit : public ModuleLoader {
ArrayRef<RemappedFile> RemappedFiles = None,
IntrusiveRefCntPtr<vfs::FileSystem> VFS = nullptr);
/// \brief Free data that will be re-generated on the next parse.
///
/// Preamble-related data is not affected.
void ResetForParse();
/// \brief Perform code completion at the given file, line, and
/// column within this translation unit.
///

View File

@ -283,6 +283,44 @@ class Preprocessor {
/// This is used when loading a precompiled preamble.
std::pair<int, bool> SkipMainFilePreamble;
class PreambleConditionalStackStore {
enum State {
Off = 0,
Recording = 1,
Replaying = 2,
};
public:
PreambleConditionalStackStore() : ConditionalStackState(Off) {}
void startRecording() { ConditionalStackState = Recording; }
void startReplaying() { ConditionalStackState = Replaying; }
bool isRecording() const { return ConditionalStackState == Recording; }
bool isReplaying() const { return ConditionalStackState == Replaying; }
ArrayRef<PPConditionalInfo> getStack() const {
return ConditionalStack;
}
void doneReplaying() {
ConditionalStack.clear();
ConditionalStackState = Off;
}
void setStack(ArrayRef<PPConditionalInfo> s) {
if (!isRecording() && !isReplaying())
return;
ConditionalStack.clear();
ConditionalStack.append(s.begin(), s.end());
}
bool hasRecordedPreamble() const { return !ConditionalStack.empty(); }
private:
SmallVector<PPConditionalInfo, 4> ConditionalStack;
State ConditionalStackState;
} PreambleConditionalStack;
/// \brief The current top of the stack that we're lexing from if
/// not expanding a macro and we are lexing directly from source code.
///
@ -1695,6 +1733,11 @@ class Preprocessor {
/// \brief Return true if we're in the top-level file, not in a \#include.
bool isInPrimaryFile() const;
/// \brief Return true if we're in the main file (specifically, if we are 0
/// (zero) levels deep \#include. This is used by the lexer to determine if
/// it needs to generate errors about unterminated \#if directives.
bool isInMainFile() const;
/// \brief Handle cases where the \#include name is expanded
/// from a macro as multiple tokens, which need to be glued together.
///
@ -1932,6 +1975,27 @@ class Preprocessor {
Module *M,
SourceLocation MLoc);
bool isRecordingPreamble() const {
return PreambleConditionalStack.isRecording();
}
bool hasRecordedPreamble() const {
return PreambleConditionalStack.hasRecordedPreamble();
}
ArrayRef<PPConditionalInfo> getPreambleConditionalStack() const {
return PreambleConditionalStack.getStack();
}
void setRecordedPreambleConditionalStack(ArrayRef<PPConditionalInfo> s) {
PreambleConditionalStack.setStack(s);
}
void setReplayablePreambleConditionalStack(ArrayRef<PPConditionalInfo> s) {
PreambleConditionalStack.startReplaying();
PreambleConditionalStack.setStack(s);
}
private:
// Macro handling.
void HandleDefineDirective(Token &Tok, bool ImmediatelyAfterTopLevelIfndef);

View File

@ -17,6 +17,7 @@
#include "clang/Lex/MultipleIncludeOpt.h"
#include "clang/Lex/Token.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
namespace clang {
@ -176,6 +177,11 @@ class PreprocessorLexer {
conditional_iterator conditional_end() const {
return ConditionalStack.end();
}
void setConditionalLevels(ArrayRef<PPConditionalInfo> CL) {
ConditionalStack.clear();
ConditionalStack.append(CL.begin(), CL.end());
}
};
} // end namespace clang

View File

@ -80,7 +80,14 @@ class PreprocessorOptions {
/// The boolean indicates whether the preamble ends at the start of a new
/// line.
std::pair<unsigned, bool> PrecompiledPreambleBytes;
/// \brief True indicates that a preamble is being generated.
///
/// When the lexer is done, one of the things that need to be preserved is the
/// conditional #if stack, so the ASTWriter/ASTReader can save/restore it when
/// processing the rest of the file.
bool GeneratePreamble;
/// The implicit PTH input included at the start of the translation unit, or
/// empty.
std::string ImplicitPTHInclude;
@ -144,6 +151,7 @@ class PreprocessorOptions {
AllowPCHWithCompilerErrors(false),
DumpDeserializedPCHDecls(false),
PrecompiledPreambleBytes(0, true),
GeneratePreamble(false),
RemappedFilesKeepOriginalName(true),
RetainRemappedFileBuffers(false),
ObjCXXARCStandardLibrary(ARCXX_nolib) { }

View File

@ -607,6 +607,9 @@ namespace clang {
/// \brief Record code for \#pragma pack options.
PACK_PRAGMA_OPTIONS = 61,
/// \brief The stack of open #ifs/#ifdefs recorded in a preamble.
PP_CONDITIONAL_STACK = 62,
};
/// \brief Record types used within a source manager block.

View File

@ -279,15 +279,15 @@ def VirtualCallChecker : Checker<"VirtualCall">,
let ParentPackage = CplusplusAlpha in {
def IteratorRangeChecker : Checker<"IteratorRange">,
HelpText<"Check for iterators used outside their valid ranges">,
DescFile<"IteratorChecker.cpp">;
def MisusedMovedObjectChecker: Checker<"MisusedMovedObject">,
HelpText<"Method calls on a moved-from object and copying a moved-from "
"object will be reported">,
DescFile<"MisusedMovedObjectChecker.cpp">;
def IteratorPastEndChecker : Checker<"IteratorPastEnd">,
HelpText<"Check iterators used past end">,
DescFile<"IteratorPastEndChecker.cpp">;
} // end: "alpha.cplusplus"

View File

@ -43,6 +43,7 @@ typedef std::unique_ptr<ConstraintManager>(*ConstraintManagerCreator)(
ProgramStateManager &, SubEngine *);
typedef std::unique_ptr<StoreManager>(*StoreManagerCreator)(
ProgramStateManager &);
typedef llvm::ImmutableMap<const SubRegion*, TaintTagType> TaintedSubRegions;
//===----------------------------------------------------------------------===//
// ProgramStateTrait - Traits used by the Generic Data Map of a ProgramState.
@ -343,6 +344,9 @@ class ProgramState : public llvm::FoldingSetNode {
ProgramStateRef addTaint(const Stmt *S, const LocationContext *LCtx,
TaintTagType Kind = TaintTagGeneric) const;
/// Create a new state in which the value is marked as tainted.
ProgramStateRef addTaint(SVal V, TaintTagType Kind = TaintTagGeneric) const;
/// Create a new state in which the symbol is marked as tainted.
ProgramStateRef addTaint(SymbolRef S,
TaintTagType Kind = TaintTagGeneric) const;
@ -351,6 +355,14 @@ class ProgramState : public llvm::FoldingSetNode {
ProgramStateRef addTaint(const MemRegion *R,
TaintTagType Kind = TaintTagGeneric) const;
/// Create a new state in a which a sub-region of a given symbol is tainted.
/// This might be necessary when referring to regions that can not have an
/// individual symbol, e.g. if they are represented by the default binding of
/// a LazyCompoundVal.
ProgramStateRef addPartialTaint(SymbolRef ParentSym,
const SubRegion *SubRegion,
TaintTagType Kind = TaintTagGeneric) const;
/// Check if the statement is tainted in the current state.
bool isTainted(const Stmt *S, const LocationContext *LCtx,
TaintTagType Kind = TaintTagGeneric) const;
@ -453,6 +465,7 @@ class ProgramStateManager {
std::unique_ptr<ConstraintManager> ConstraintMgr;
ProgramState::GenericDataMap::Factory GDMFactory;
TaintedSubRegions::Factory TSRFactory;
typedef llvm::DenseMap<void*,std::pair<void*,void (*)(void*)> > GDMContextsTy;
GDMContextsTy GDMContexts;

View File

@ -35,6 +35,16 @@ template<> struct ProgramStateTrait<TaintMap>
static void *GDMIndex() { static int index = 0; return &index; }
};
/// The GDM component mapping derived symbols' parent symbols to their
/// underlying regions. This is used to efficiently check whether a symbol is
/// tainted when it represents a sub-region of a tainted symbol.
struct DerivedSymTaint {};
typedef llvm::ImmutableMap<SymbolRef, TaintedSubRegions> DerivedSymTaintImpl;
template<> struct ProgramStateTrait<DerivedSymTaint>
: public ProgramStatePartialTrait<DerivedSymTaintImpl> {
static void *GDMIndex() { static int index; return &index; }
};
class TaintManager {
TaintManager() {}

View File

@ -578,8 +578,7 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
EmitBlock(FinalBB);
CurCoro.Data->CurrentAwaitKind = AwaitKind::Final;
EmitStmt(S.getFinalSuspendStmt());
}
else {
} else {
// We don't need FinalBB. Emit it to make sure the block is deleted.
EmitBlock(FinalBB, /*IsFinished=*/true);
}

View File

@ -1036,8 +1036,6 @@ static void checkAndSanitizeDiags(SmallVectorImpl<StoredDiagnostic> &
bool ASTUnit::Parse(std::shared_ptr<PCHContainerOperations> PCHContainerOps,
std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer,
IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
SavedMainFileBuffer.reset();
if (!Invocation)
return true;
@ -1090,17 +1088,11 @@ bool ASTUnit::Parse(std::shared_ptr<PCHContainerOperations> PCHContainerOps,
Clang->createFileManager();
FileMgr = &Clang->getFileManager();
}
ResetForParse();
SourceMgr = new SourceManager(getDiagnostics(), *FileMgr,
UserFilesAreVolatile);
TheSema.reset();
Ctx = nullptr;
PP = nullptr;
Reader = nullptr;
// Clear out old caches and data.
TopLevelDecls.clear();
clearFileLevelDecls();
if (!OverrideMainBuffer) {
checkAndRemoveNonDriverDiags(StoredDiagnostics);
TopLevelDeclsInPreamble.clear();
@ -1999,6 +1991,7 @@ ASTUnit *ASTUnit::LoadFromCommandLine(
PreprocessorOptions &PPOpts = CI->getPreprocessorOpts();
PPOpts.RemappedFilesKeepOriginalName = RemappedFilesKeepOriginalName;
PPOpts.AllowPCHWithCompilerErrors = AllowPCHWithCompilerErrors;
PPOpts.GeneratePreamble = PrecompilePreambleAfterNParses != 0;
// Override the resources path.
CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath;
@ -2115,6 +2108,19 @@ bool ASTUnit::Reparse(std::shared_ptr<PCHContainerOperations> PCHContainerOps,
return Result;
}
void ASTUnit::ResetForParse() {
SavedMainFileBuffer.reset();
SourceMgr.reset();
TheSema.reset();
Ctx.reset();
PP.reset();
Reader.reset();
TopLevelDecls.clear();
clearFileLevelDecls();
}
//----------------------------------------------------------------------------//
// Code completion
//----------------------------------------------------------------------------//

View File

@ -506,7 +506,7 @@ void SDiagsWriter::EmitBlockInfoBlock() {
Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME));
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped file ID.
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Size.
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Modifcation time.
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Modification time.
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text.
Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,

View File

@ -2887,87 +2887,79 @@ static __inline__ vector double __ATTRS_o_ai vec_cpsgn(vector double __a,
/* vec_ctf */
static __inline__ vector float __ATTRS_o_ai vec_ctf(vector int __a, int __b) {
return __builtin_altivec_vcfsx(__a, __b);
}
static __inline__ vector float __ATTRS_o_ai vec_ctf(vector unsigned int __a,
int __b) {
return __builtin_altivec_vcfux((vector int)__a, __b);
}
#ifdef __VSX__
static __inline__ vector double __ATTRS_o_ai
vec_ctf(vector unsigned long long __a, int __b) {
vector double __ret = __builtin_convertvector(__a, vector double);
__ret *= (vector double)(vector unsigned long long)((0x3ffULL - __b) << 52);
return __ret;
}
static __inline__ vector double __ATTRS_o_ai
vec_ctf(vector signed long long __a, int __b) {
vector double __ret = __builtin_convertvector(__a, vector double);
__ret *= (vector double)(vector unsigned long long)((0x3ffULL - __b) << 52);
return __ret;
}
#define vec_ctf(__a, __b) \
_Generic((__a), vector int \
: (vector float)__builtin_altivec_vcfsx((__a), (__b)), \
vector unsigned int \
: (vector float)__builtin_altivec_vcfux((vector int)(__a), (__b)), \
vector unsigned long long \
: (__builtin_convertvector((vector unsigned long long)(__a), \
vector double) * \
(vector double)(vector unsigned long long)((0x3ffULL - (__b)) \
<< 52)), \
vector signed long long \
: (__builtin_convertvector((vector signed long long)(__a), \
vector double) * \
(vector double)(vector unsigned long long)((0x3ffULL - (__b)) \
<< 52)))
#else
#define vec_ctf(__a, __b) \
_Generic((__a), vector int \
: (vector float)__builtin_altivec_vcfsx((__a), (__b)), \
vector unsigned int \
: (vector float)__builtin_altivec_vcfux((vector int)(__a), (__b)))
#endif
/* vec_vcfsx */
static __inline__ vector float __attribute__((__always_inline__))
vec_vcfsx(vector int __a, int __b) {
return __builtin_altivec_vcfsx(__a, __b);
}
#define vec_vcfux __builtin_altivec_vcfux
/* vec_vcfux */
static __inline__ vector float __attribute__((__always_inline__))
vec_vcfux(vector unsigned int __a, int __b) {
return __builtin_altivec_vcfux((vector int)__a, __b);
}
#define vec_vcfsx(__a, __b) __builtin_altivec_vcfsx((vector int)(__a), (__b))
/* vec_cts */
static __inline__ vector int __ATTRS_o_ai vec_cts(vector float __a, int __b) {
return __builtin_altivec_vctsxs(__a, __b);
}
#ifdef __VSX__
static __inline__ vector signed long long __ATTRS_o_ai
vec_cts(vector double __a, int __b) {
__a *= (vector double)(vector unsigned long long)((0x3ffULL + __b) << 52);
return __builtin_convertvector(__a, vector signed long long);
}
#define vec_cts(__a, __b) \
_Generic((__a), vector float \
: __builtin_altivec_vctsxs((__a), (__b)), vector double \
: __extension__({ \
vector double __ret = \
(__a) * \
(vector double)(vector unsigned long long)((0x3ffULL + (__b)) \
<< 52); \
__builtin_convertvector(__ret, vector signed long long); \
}))
#else
#define vec_cts __builtin_altivec_vctsxs
#endif
/* vec_vctsxs */
static __inline__ vector int __attribute__((__always_inline__))
vec_vctsxs(vector float __a, int __b) {
return __builtin_altivec_vctsxs(__a, __b);
}
#define vec_vctsxs __builtin_altivec_vctsxs
/* vec_ctu */
static __inline__ vector unsigned int __ATTRS_o_ai vec_ctu(vector float __a,
int __b) {
return __builtin_altivec_vctuxs(__a, __b);
}
#ifdef __VSX__
static __inline__ vector unsigned long long __ATTRS_o_ai
vec_ctu(vector double __a, int __b) {
__a *= (vector double)(vector unsigned long long)((0x3ffULL + __b) << 52);
return __builtin_convertvector(__a, vector unsigned long long);
}
#define vec_ctu(__a, __b) \
_Generic((__a), vector float \
: __builtin_altivec_vctuxs((__a), (__b)), vector double \
: __extension__({ \
vector double __ret = \
(__a) * \
(vector double)(vector unsigned long long)((0x3ffULL + __b) \
<< 52); \
__builtin_convertvector(__ret, vector unsigned long long); \
}))
#else
#define vec_ctu __builtin_altivec_vctuxs
#endif
/* vec_vctuxs */
static __inline__ vector unsigned int __attribute__((__always_inline__))
vec_vctuxs(vector float __a, int __b) {
return __builtin_altivec_vctuxs(__a, __b);
}
#define vec_vctuxs __builtin_altivec_vctuxs
/* vec_signed */

View File

@ -550,8 +550,6 @@ namespace {
enum PreambleDirectiveKind {
PDK_Skipped,
PDK_StartIf,
PDK_EndIf,
PDK_Unknown
};
@ -574,8 +572,6 @@ std::pair<unsigned, bool> Lexer::ComputePreamble(StringRef Buffer,
bool InPreprocessorDirective = false;
Token TheTok;
Token IfStartTok;
unsigned IfCount = 0;
SourceLocation ActiveCommentLoc;
unsigned MaxLineOffset = 0;
@ -658,33 +654,18 @@ std::pair<unsigned, bool> Lexer::ComputePreamble(StringRef Buffer,
.Case("sccs", PDK_Skipped)
.Case("assert", PDK_Skipped)
.Case("unassert", PDK_Skipped)
.Case("if", PDK_StartIf)
.Case("ifdef", PDK_StartIf)
.Case("ifndef", PDK_StartIf)
.Case("if", PDK_Skipped)
.Case("ifdef", PDK_Skipped)
.Case("ifndef", PDK_Skipped)
.Case("elif", PDK_Skipped)
.Case("else", PDK_Skipped)
.Case("endif", PDK_EndIf)
.Case("endif", PDK_Skipped)
.Default(PDK_Unknown);
switch (PDK) {
case PDK_Skipped:
continue;
case PDK_StartIf:
if (IfCount == 0)
IfStartTok = HashTok;
++IfCount;
continue;
case PDK_EndIf:
// Mismatched #endif. The preamble ends here.
if (IfCount == 0)
break;
--IfCount;
continue;
case PDK_Unknown:
// We don't know what this directive is; stop at the '#'.
break;
@ -705,16 +686,13 @@ std::pair<unsigned, bool> Lexer::ComputePreamble(StringRef Buffer,
} while (true);
SourceLocation End;
if (IfCount)
End = IfStartTok.getLocation();
else if (ActiveCommentLoc.isValid())
if (ActiveCommentLoc.isValid())
End = ActiveCommentLoc; // don't truncate a decl comment.
else
End = TheTok.getLocation();
return std::make_pair(End.getRawEncoding() - StartLoc.getRawEncoding(),
IfCount? IfStartTok.isAtStartOfLine()
: TheTok.isAtStartOfLine());
TheTok.isAtStartOfLine());
}
/// AdvanceToTokenCharacter - Given a location that specifies the start of a
@ -2570,6 +2548,11 @@ bool Lexer::LexEndOfFile(Token &Result, const char *CurPtr) {
return true;
}
if (PP->isRecordingPreamble() && !PP->isInMainFile()) {
PP->setRecordedPreambleConditionalStack(ConditionalStack);
ConditionalStack.clear();
}
// Issue diagnostics for unterminated #if and missing newline.
// If we are in a #if directive, emit an error.

View File

@ -1906,6 +1906,25 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
}
}
// The #included file will be considered to be a system header if either it is
// in a system include directory, or if the #includer is a system include
// header.
SrcMgr::CharacteristicKind FileCharacter =
SourceMgr.getFileCharacteristic(FilenameTok.getLocation());
if (File)
FileCharacter = std::max(HeaderInfo.getFileDirFlavor(File), FileCharacter);
// Ask HeaderInfo if we should enter this #include file. If not, #including
// this file will have no effect.
bool SkipHeader = false;
if (ShouldEnter && File &&
!HeaderInfo.ShouldEnterIncludeFile(*this, File, isImport,
getLangOpts().Modules,
SuggestedModule.getModule())) {
ShouldEnter = false;
SkipHeader = true;
}
if (Callbacks) {
// Notify the callback object that we've seen an inclusion directive.
Callbacks->InclusionDirective(
@ -1913,18 +1932,13 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, isAngled,
FilenameRange, File, SearchPath, RelativePath,
ShouldEnter ? nullptr : SuggestedModule.getModule());
if (SkipHeader && !SuggestedModule.getModule())
Callbacks->FileSkipped(*File, FilenameTok, FileCharacter);
}
if (!File)
return;
// The #included file will be considered to be a system header if either it is
// in a system include directory, or if the #includer is a system include
// header.
SrcMgr::CharacteristicKind FileCharacter =
std::max(HeaderInfo.getFileDirFlavor(File),
SourceMgr.getFileCharacteristic(FilenameTok.getLocation()));
// FIXME: If we have a suggested module, and we've already visited this file,
// don't bother entering it again. We know it has no further effect.
@ -1964,19 +1978,6 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
}
}
// Ask HeaderInfo if we should enter this #include file. If not, #including
// this file will have no effect.
bool SkipHeader = false;
if (ShouldEnter &&
!HeaderInfo.ShouldEnterIncludeFile(*this, File, isImport,
getLangOpts().Modules,
SuggestedModule.getModule())) {
ShouldEnter = false;
SkipHeader = true;
if (Callbacks)
Callbacks->FileSkipped(*File, FilenameTok, FileCharacter);
}
// If we don't need to enter the file, stop now.
if (!ShouldEnter) {
// If this is a module import, make it visible if needed.

View File

@ -46,6 +46,12 @@ bool Preprocessor::isInPrimaryFile() const {
});
}
bool Preprocessor::isInMainFile() const {
if (IsFileLexer())
return IncludeMacroStack.size() == 0;
return true;
}
/// getCurrentLexer - Return the current file lexer being lexed from. Note
/// that this ignores any potentially active macro expansions and _Pragma
/// expansions going on at the time.

View File

@ -1407,6 +1407,24 @@ struct PragmaModuleBeginHandler : public PragmaHandler {
M = NewM;
}
// If the module isn't available, it doesn't make sense to enter it.
if (!M->isAvailable()) {
Module::Requirement Requirement;
Module::UnresolvedHeaderDirective MissingHeader;
(void)M->isAvailable(PP.getLangOpts(), PP.getTargetInfo(),
Requirement, MissingHeader);
if (MissingHeader.FileNameLoc.isValid()) {
PP.Diag(MissingHeader.FileNameLoc, diag::err_module_header_missing)
<< MissingHeader.IsUmbrella << MissingHeader.FileName;
} else {
PP.Diag(M->DefinitionLoc, diag::err_module_unavailable)
<< M->getFullModuleName() << Requirement.second << Requirement.first;
}
PP.Diag(BeginLoc, diag::note_pp_module_begin_here)
<< M->getTopLevelModuleName();
return;
}
// Enter the scope of the submodule.
PP.EnterSubmodule(M, BeginLoc, /*ForPragma*/true);
PP.EnterAnnotationToken(SourceRange(BeginLoc, ModuleName.back().second),

View File

@ -150,6 +150,9 @@ Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
Ident_GetExceptionInfo = Ident_GetExceptionCode = nullptr;
Ident_AbnormalTermination = nullptr;
}
if (this->PPOpts->GeneratePreamble)
PreambleConditionalStack.startRecording();
}
Preprocessor::~Preprocessor() {
@ -532,6 +535,12 @@ void Preprocessor::EnterMainSourceFile() {
// Start parsing the predefines.
EnterSourceFile(FID, nullptr, SourceLocation());
// Restore the conditional stack from the preamble, if there is one.
if (PreambleConditionalStack.isReplaying()) {
CurPPLexer->setConditionalLevels(PreambleConditionalStack.getStack());
PreambleConditionalStack.doneReplaying();
}
}
void Preprocessor::EndSourceFile() {

View File

@ -12509,6 +12509,9 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc,
unsigned diag_id;
if (II.getName().startswith("__builtin_"))
diag_id = diag::warn_builtin_unknown;
// OpenCL v2.0 s6.9.u - Implicit function declaration is not supported.
else if (getLangOpts().OpenCL)
diag_id = diag::err_opencl_implicit_function_decl;
else if (getLangOpts().C99)
diag_id = diag::ext_implicit_function_decl;
else

View File

@ -8074,28 +8074,38 @@ QualType Sema::InvalidLogicalVectorOperands(SourceLocation Loc, ExprResult &LHS,
/// rank; for C, Obj-C, and C++ we allow any real scalar conversion except
/// for float->int.
///
/// OpenCL V2.0 6.2.6.p2:
/// An error shall occur if any scalar operand type has greater rank
/// than the type of the vector element.
///
/// \param scalar - if non-null, actually perform the conversions
/// \return true if the operation fails (but without diagnosing the failure)
static bool tryVectorConvertAndSplat(Sema &S, ExprResult *scalar,
QualType scalarTy,
QualType vectorEltTy,
QualType vectorTy) {
QualType vectorTy,
unsigned &DiagID) {
// The conversion to apply to the scalar before splatting it,
// if necessary.
CastKind scalarCast = CK_Invalid;
if (vectorEltTy->isIntegralType(S.Context)) {
if (!scalarTy->isIntegralType(S.Context))
if (S.getLangOpts().OpenCL && (scalarTy->isRealFloatingType() ||
(scalarTy->isIntegerType() &&
S.Context.getIntegerTypeOrder(vectorEltTy, scalarTy) < 0))) {
DiagID = diag::err_opencl_scalar_type_rank_greater_than_vector_type;
return true;
if (S.getLangOpts().OpenCL &&
S.Context.getIntegerTypeOrder(vectorEltTy, scalarTy) < 0)
}
if (!scalarTy->isIntegralType(S.Context))
return true;
scalarCast = CK_IntegralCast;
} else if (vectorEltTy->isRealFloatingType()) {
if (scalarTy->isRealFloatingType()) {
if (S.getLangOpts().OpenCL &&
S.Context.getFloatingTypeOrder(vectorEltTy, scalarTy) < 0)
S.Context.getFloatingTypeOrder(vectorEltTy, scalarTy) < 0) {
DiagID = diag::err_opencl_scalar_type_rank_greater_than_vector_type;
return true;
}
scalarCast = CK_FloatingCast;
}
else if (scalarTy->isIntegralType(S.Context))
@ -8341,10 +8351,12 @@ QualType Sema::CheckVectorOperands(ExprResult &LHS, ExprResult &RHS,
// If there's a vector type and a scalar, try to convert the scalar to
// the vector element type and splat.
unsigned DiagID = diag::err_typecheck_vector_not_convertable;
if (!RHSVecType) {
if (isa<ExtVectorType>(LHSVecType)) {
if (!tryVectorConvertAndSplat(*this, &RHS, RHSType,
LHSVecType->getElementType(), LHSType))
LHSVecType->getElementType(), LHSType,
DiagID))
return LHSType;
} else {
if (!tryGCCVectorConvertAndSplat(*this, &RHS, &LHS))
@ -8355,7 +8367,7 @@ QualType Sema::CheckVectorOperands(ExprResult &LHS, ExprResult &RHS,
if (isa<ExtVectorType>(RHSVecType)) {
if (!tryVectorConvertAndSplat(*this, (IsCompAssign ? nullptr : &LHS),
LHSType, RHSVecType->getElementType(),
RHSType))
RHSType, DiagID))
return RHSType;
} else {
if (LHS.get()->getValueKind() == VK_LValue ||
@ -8431,7 +8443,7 @@ QualType Sema::CheckVectorOperands(ExprResult &LHS, ExprResult &RHS,
}
// Otherwise, use the generic diagnostic.
Diag(Loc, diag::err_typecheck_vector_not_convertable)
Diag(Loc, DiagID)
<< LHSType << RHSType
<< LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
return QualType();

View File

@ -5929,16 +5929,17 @@ StmtResult Sema::ActOnOpenMPTargetParallelForDirective(
B, DSAStack->isCancelRegion());
}
/// \brief Check for existence of a map clause in the list of clauses.
static bool HasMapClause(ArrayRef<OMPClause *> Clauses) {
for (ArrayRef<OMPClause *>::iterator I = Clauses.begin(), E = Clauses.end();
I != E; ++I) {
if (*I != nullptr && (*I)->getClauseKind() == OMPC_map) {
return true;
}
}
/// Check for existence of a map clause in the list of clauses.
static bool hasClauses(ArrayRef<OMPClause *> Clauses,
const OpenMPClauseKind K) {
return llvm::any_of(
Clauses, [K](const OMPClause *C) { return C->getClauseKind() == K; });
}
return false;
template <typename... Params>
static bool hasClauses(ArrayRef<OMPClause *> Clauses, const OpenMPClauseKind K,
const Params... ClauseTypes) {
return hasClauses(Clauses, K) || hasClauses(Clauses, ClauseTypes...);
}
StmtResult Sema::ActOnOpenMPTargetDataDirective(ArrayRef<OMPClause *> Clauses,
@ -5952,8 +5953,9 @@ StmtResult Sema::ActOnOpenMPTargetDataDirective(ArrayRef<OMPClause *> Clauses,
// OpenMP [2.10.1, Restrictions, p. 97]
// At least one map clause must appear on the directive.
if (!HasMapClause(Clauses)) {
Diag(StartLoc, diag::err_omp_no_map_for_directive)
if (!hasClauses(Clauses, OMPC_map, OMPC_use_device_ptr)) {
Diag(StartLoc, diag::err_omp_no_clause_for_directive)
<< "'map' or 'use_device_ptr'"
<< getOpenMPDirectiveName(OMPD_target_data);
return StmtError();
}
@ -5970,9 +5972,9 @@ Sema::ActOnOpenMPTargetEnterDataDirective(ArrayRef<OMPClause *> Clauses,
SourceLocation EndLoc) {
// OpenMP [2.10.2, Restrictions, p. 99]
// At least one map clause must appear on the directive.
if (!HasMapClause(Clauses)) {
Diag(StartLoc, diag::err_omp_no_map_for_directive)
<< getOpenMPDirectiveName(OMPD_target_enter_data);
if (!hasClauses(Clauses, OMPC_map)) {
Diag(StartLoc, diag::err_omp_no_clause_for_directive)
<< "'map'" << getOpenMPDirectiveName(OMPD_target_enter_data);
return StmtError();
}
@ -5986,9 +5988,9 @@ Sema::ActOnOpenMPTargetExitDataDirective(ArrayRef<OMPClause *> Clauses,
SourceLocation EndLoc) {
// OpenMP [2.10.3, Restrictions, p. 102]
// At least one map clause must appear on the directive.
if (!HasMapClause(Clauses)) {
Diag(StartLoc, diag::err_omp_no_map_for_directive)
<< getOpenMPDirectiveName(OMPD_target_exit_data);
if (!hasClauses(Clauses, OMPC_map)) {
Diag(StartLoc, diag::err_omp_no_clause_for_directive)
<< "'map'" << getOpenMPDirectiveName(OMPD_target_exit_data);
return StmtError();
}
@ -5998,12 +6000,7 @@ Sema::ActOnOpenMPTargetExitDataDirective(ArrayRef<OMPClause *> Clauses,
StmtResult Sema::ActOnOpenMPTargetUpdateDirective(ArrayRef<OMPClause *> Clauses,
SourceLocation StartLoc,
SourceLocation EndLoc) {
bool seenMotionClause = false;
for (auto *C : Clauses) {
if (C->getClauseKind() == OMPC_to || C->getClauseKind() == OMPC_from)
seenMotionClause = true;
}
if (!seenMotionClause) {
if (!hasClauses(Clauses, OMPC_to, OMPC_from)) {
Diag(StartLoc, diag::err_omp_at_least_one_motion_clause_required);
return StmtError();
}

View File

@ -2925,6 +2925,21 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
}
break;
case PP_CONDITIONAL_STACK:
if (!Record.empty()) {
SmallVector<PPConditionalInfo, 4> ConditionalStack;
for (unsigned Idx = 0, N = Record.size() - 1; Idx < N; /* in loop */) {
auto Loc = ReadSourceLocation(F, Record, Idx);
bool WasSkipping = Record[Idx++];
bool FoundNonSkip = Record[Idx++];
bool FoundElse = Record[Idx++];
ConditionalStack.push_back(
{Loc, WasSkipping, FoundNonSkip, FoundElse});
}
PP.setReplayablePreambleConditionalStack(ConditionalStack);
}
break;
case PP_COUNTER_VALUE:
if (!Record.empty() && Listener)
Listener->ReadCounter(F, Record[0]);

View File

@ -1093,6 +1093,7 @@ void ASTWriter::WriteBlockInfoBlock() {
RECORD(UNUSED_LOCAL_TYPEDEF_NAME_CANDIDATES);
RECORD(DELETE_EXPRS_TO_ANALYZE);
RECORD(CUDA_PRAGMA_FORCE_HOST_DEVICE_DEPTH);
RECORD(PP_CONDITIONAL_STACK);
// SourceManager Block.
BLOCK(SOURCE_MANAGER_BLOCK);
@ -2302,6 +2303,18 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) {
Stream.EmitRecord(PP_COUNTER_VALUE, Record);
}
if (PP.isRecordingPreamble() && PP.hasRecordedPreamble()) {
assert(!IsModule);
for (const auto &Cond : PP.getPreambleConditionalStack()) {
AddSourceLocation(Cond.IfLoc, Record);
Record.push_back(Cond.WasSkipping);
Record.push_back(Cond.FoundNonSkip);
Record.push_back(Cond.FoundElse);
}
Stream.EmitRecord(PP_CONDITIONAL_STACK, Record);
Record.clear();
}
// Enter the preprocessor block.
Stream.EnterSubblock(PREPROCESSOR_BLOCK_ID, 3);

View File

@ -39,7 +39,7 @@ add_clang_library(clangStaticAnalyzerCheckers
GenericTaintChecker.cpp
GTestChecker.cpp
IdenticalExprChecker.cpp
IteratorPastEndChecker.cpp
IteratorChecker.cpp
IvarInvalidationChecker.cpp
LLVMConventionsChecker.cpp
LocalizationChecker.cpp

View File

@ -65,21 +65,8 @@ class GenericTaintChecker : public Checker< check::PostStmt<CallExpr>,
/// and thus, is tainted.
static bool isStdin(const Expr *E, CheckerContext &C);
/// This is called from getPointedToSymbol() to resolve symbol references for
/// the region underlying a LazyCompoundVal. This is the default binding
/// for the LCV, which could be a conjured symbol from a function call that
/// initialized the region. It only returns the conjured symbol if the LCV
/// covers the entire region, e.g. we avoid false positives by not returning
/// a default bindingc for an entire struct if the symbol for only a single
/// field or element within it is requested.
// TODO: Return an appropriate symbol for sub-fields/elements of an LCV so
// that they are also appropriately tainted.
static SymbolRef getLCVSymbol(CheckerContext &C,
nonloc::LazyCompoundVal &LCV);
/// \brief Given a pointer argument, get the symbol of the value it contains
/// (points to).
static SymbolRef getPointedToSymbol(CheckerContext &C, const Expr *Arg);
/// \brief Given a pointer argument, return the value it points to.
static Optional<SVal> getPointedToSVal(CheckerContext &C, const Expr *Arg);
/// Functions defining the attack surface.
typedef ProgramStateRef (GenericTaintChecker::*FnCheck)(const CallExpr *,
@ -186,9 +173,14 @@ class GenericTaintChecker : public Checker< check::PostStmt<CallExpr>,
static inline bool isTaintedOrPointsToTainted(const Expr *E,
ProgramStateRef State,
CheckerContext &C) {
return (State->isTainted(E, C.getLocationContext()) || isStdin(E, C) ||
(E->getType().getTypePtr()->isPointerType() &&
State->isTainted(getPointedToSymbol(C, E))));
if (State->isTainted(E, C.getLocationContext()) || isStdin(E, C))
return true;
if (!E->getType().getTypePtr()->isPointerType())
return false;
Optional<SVal> V = getPointedToSVal(C, E);
return (V && State->isTainted(*V));
}
/// \brief Pre-process a function which propagates taint according to the
@ -400,9 +392,9 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE,
if (CE->getNumArgs() < (ArgNum + 1))
return false;
const Expr* Arg = CE->getArg(ArgNum);
SymbolRef Sym = getPointedToSymbol(C, Arg);
if (Sym)
State = State->addTaint(Sym);
Optional<SVal> V = getPointedToSVal(C, Arg);
if (V)
State = State->addTaint(*V);
}
// Clear up the taint info from the state.
@ -473,47 +465,20 @@ bool GenericTaintChecker::checkPre(const CallExpr *CE, CheckerContext &C) const{
return false;
}
SymbolRef GenericTaintChecker::getLCVSymbol(CheckerContext &C,
nonloc::LazyCompoundVal &LCV) {
StoreManager &StoreMgr = C.getStoreManager();
// getLCVSymbol() is reached in a PostStmt so we can always expect a default
// binding to exist if one is present.
if (Optional<SVal> binding = StoreMgr.getDefaultBinding(LCV)) {
SymbolRef Sym = binding->getAsSymbol();
if (!Sym)
return nullptr;
// If the LCV covers an entire base region return the default conjured symbol.
if (LCV.getRegion() == LCV.getRegion()->getBaseRegion())
return Sym;
}
// Otherwise, return a nullptr as there's not yet a functional way to taint
// sub-regions of LCVs.
return nullptr;
}
SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C,
const Expr* Arg) {
Optional<SVal> GenericTaintChecker::getPointedToSVal(CheckerContext &C,
const Expr* Arg) {
ProgramStateRef State = C.getState();
SVal AddrVal = State->getSVal(Arg->IgnoreParens(), C.getLocationContext());
if (AddrVal.isUnknownOrUndef())
return nullptr;
return None;
Optional<Loc> AddrLoc = AddrVal.getAs<Loc>();
if (!AddrLoc)
return nullptr;
return None;
const PointerType *ArgTy =
dyn_cast<PointerType>(Arg->getType().getCanonicalType().getTypePtr());
SVal Val = State->getSVal(*AddrLoc,
ArgTy ? ArgTy->getPointeeType(): QualType());
if (auto LCV = Val.getAs<nonloc::LazyCompoundVal>())
return getLCVSymbol(C, *LCV);
return Val.getAsSymbol();
return State->getSVal(*AddrLoc, ArgTy ? ArgTy->getPointeeType(): QualType());
}
ProgramStateRef
@ -633,9 +598,9 @@ ProgramStateRef GenericTaintChecker::postScanf(const CallExpr *CE,
// The arguments are pointer arguments. The data they are pointing at is
// tainted after the call.
const Expr* Arg = CE->getArg(i);
SymbolRef Sym = getPointedToSymbol(C, Arg);
if (Sym)
State = State->addTaint(Sym);
Optional<SVal> V = getPointedToSVal(C, Arg);
if (V)
State = State->addTaint(*V);
}
return State;
}
@ -710,10 +675,10 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E,
// Check for taint.
ProgramStateRef State = C.getState();
const SymbolRef PointedToSym = getPointedToSymbol(C, E);
Optional<SVal> PointedToSVal = getPointedToSVal(C, E);
SVal TaintedSVal;
if (State->isTainted(PointedToSym))
TaintedSVal = nonloc::SymbolVal(PointedToSym);
if (PointedToSVal && State->isTainted(*PointedToSVal))
TaintedSVal = *PointedToSVal;
else if (State->isTainted(E, C.getLocationContext()))
TaintedSVal = C.getSVal(E);
else

View File

@ -0,0 +1,833 @@
//===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Defines a checker for using iterators outside their range (past end). Usage
// means here dereferencing, incrementing etc.
//
//===----------------------------------------------------------------------===//
//
// In the code, iterator can be represented as a:
// * type-I: typedef-ed pointer. Operations over such iterator, such as
// comparisons or increments, are modeled straightforwardly by the
// analyzer.
// * type-II: structure with its method bodies available. Operations over such
// iterator are inlined by the analyzer, and results of modeling
// these operations are exposing implementation details of the
// iterators, which is not necessarily helping.
// * type-III: completely opaque structure. Operations over such iterator are
// modeled conservatively, producing conjured symbols everywhere.
//
// To handle all these types in a common way we introduce a structure called
// IteratorPosition which is an abstraction of the position the iterator
// represents using symbolic expressions. The checker handles all the
// operations on this structure.
//
// Additionally, depending on the circumstances, operators of types II and III
// can be represented as:
// * type-IIa, type-IIIa: conjured structure symbols - when returned by value
// from conservatively evaluated methods such as
// `.begin()`.
// * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
// variables or temporaries, when the iterator object is
// currently treated as an lvalue.
// * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
// iterator object is treated as an rvalue taken of a
// particular lvalue, eg. a copy of "type-a" iterator
// object, or an iterator that existed before the
// analysis has started.
//
// To handle any of these three different representations stored in an SVal we
// use setter and getters functions which separate the three cases. To store
// them we use a pointer union of symbol and memory region.
//
// The checker works the following way: We record the past-end iterator for
// all containers whenever their `.end()` is called. Since the Constraint
// Manager cannot handle SVals we need to take over its role. We post-check
// equality and non-equality comparisons and propagate the position of the
// iterator to the other side of the comparison if it is past-end and we are in
// the 'equal' branch (true-branch for `==` and false-branch for `!=`).
//
// In case of type-I or type-II iterators we get a concrete integer as a result
// of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
// this latter case we record the symbol and reload it in evalAssume() and do
// the propagation there. We also handle (maybe double) negated comparisons
// which are represented in the form of (x == 0 or x !=0 ) where x is the
// comparison itself.
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
// Abstract position of an iterator. This helps to handle all three kinds
// of operators in a common way by using a symbolic position.
struct IteratorPosition {
private:
// Container the iterator belongs to
const MemRegion *Cont;
// Abstract offset
SymbolRef Offset;
IteratorPosition(const MemRegion *C, SymbolRef Of)
: Cont(C), Offset(Of) {}
public:
const MemRegion *getContainer() const { return Cont; }
SymbolRef getOffset() const { return Offset; }
static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
return IteratorPosition(C, Of);
}
IteratorPosition setTo(SymbolRef NewOf) const {
return IteratorPosition(Cont, NewOf);
}
bool operator==(const IteratorPosition &X) const {
return Cont == X.Cont && Offset == X.Offset;
}
bool operator!=(const IteratorPosition &X) const {
return Cont != X.Cont || Offset != X.Offset;
}
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddPointer(Cont);
ID.Add(Offset);
}
};
typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
// Structure to record the symbolic end position of a container
struct ContainerData {
private:
SymbolRef End;
ContainerData(SymbolRef E) : End(E) {}
public:
static ContainerData fromEnd(SymbolRef E) {
return ContainerData(E);
}
SymbolRef getEnd() const { return End; }
ContainerData newEnd(SymbolRef E) const { return ContainerData(E); }
bool operator==(const ContainerData &X) const {
return End == X.End;
}
bool operator!=(const ContainerData &X) const {
return End != X.End;
}
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.Add(End);
}
};
// Structure fo recording iterator comparisons. We needed to retrieve the
// original comparison expression in assumptions.
struct IteratorComparison {
private:
RegionOrSymbol Left, Right;
bool Equality;
public:
IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
: Left(L), Right(R), Equality(Eq) {}
RegionOrSymbol getLeft() const { return Left; }
RegionOrSymbol getRight() const { return Right; }
bool isEquality() const { return Equality; }
bool operator==(const IteratorComparison &X) const {
return Left == X.Left && Right == X.Right && Equality == X.Equality;
}
bool operator!=(const IteratorComparison &X) const {
return Left != X.Left || Right != X.Right || Equality != X.Equality;
}
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
};
class IteratorChecker
: public Checker<check::PreCall, check::PostCall,
check::PostStmt<MaterializeTemporaryExpr>,
check::DeadSymbols,
eval::Assume> {
std::unique_ptr<BugType> OutOfRangeBugType;
void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
const SVal &RVal, OverloadedOperatorKind Op) const;
void verifyDereference(CheckerContext &C, const SVal &Val) const;
void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
const SVal &Cont) const;
void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
const MemRegion *Cont) const;
void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
CheckerContext &C, ExplodedNode *ErrNode) const;
public:
IteratorChecker();
enum CheckKind {
CK_IteratorRangeChecker,
CK_NumCheckKinds
};
DefaultBool ChecksEnabled[CK_NumCheckKinds];
CheckName CheckNames[CK_NumCheckKinds];
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostStmt(const MaterializeTemporaryExpr *MTE,
CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
bool Assumption) const;
};
} // namespace
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
IteratorPosition)
REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
IteratorComparison)
namespace {
bool isIteratorType(const QualType &Type);
bool isIterator(const CXXRecordDecl *CRD);
bool isEndCall(const FunctionDecl *Func);
bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
bool isDereferenceOperator(OverloadedOperatorKind OK);
BinaryOperator::Opcode getOpcode(const SymExpr *SE);
const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
const ProgramStateRef processComparison(ProgramStateRef State,
RegionOrSymbol LVal,
RegionOrSymbol RVal, bool Equal);
const ProgramStateRef saveComparison(ProgramStateRef State,
const SymExpr *Condition, const SVal &LVal,
const SVal &RVal, bool Eq);
const IteratorComparison *loadComparison(ProgramStateRef State,
const SymExpr *Condition);
SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
const SymbolRef Sym);
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
const SVal &Val);
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
RegionOrSymbol RegOrSym);
ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
const IteratorPosition &Pos);
ProgramStateRef setIteratorPosition(ProgramStateRef State,
RegionOrSymbol RegOrSym,
const IteratorPosition &Pos);
ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
RegionOrSymbol RegOrSym,
const IteratorPosition &Pos, bool Equal);
ProgramStateRef relateIteratorPositions(ProgramStateRef State,
const IteratorPosition &Pos1,
const IteratorPosition &Pos2,
bool Equal);
const ContainerData *getContainerData(ProgramStateRef State,
const MemRegion *Cont);
ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
const ContainerData &CData);
bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos);
} // namespace
IteratorChecker::IteratorChecker() {
OutOfRangeBugType.reset(
new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
OutOfRangeBugType->setSuppressOnSink(true);
}
void IteratorChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
// Check for out of range access
const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!Func)
return;
if (Func->isOverloadedOperator()) {
if (ChecksEnabled[CK_IteratorRangeChecker] &&
isDereferenceOperator(Func->getOverloadedOperator())) {
// Check for dereference of out-of-range iterators
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
verifyDereference(C, InstCall->getCXXThisVal());
} else {
verifyDereference(C, Call.getArgSVal(0));
}
}
}
}
void IteratorChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
// Record new iterator positions and iterator position changes
const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!Func)
return;
if (Func->isOverloadedOperator()) {
const auto Op = Func->getOverloadedOperator();
if (isSimpleComparisonOperator(Op)) {
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
Call.getArgSVal(0), Op);
} else {
handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
Call.getArgSVal(1), Op);
}
}
} else {
const auto *OrigExpr = Call.getOriginExpr();
if (!OrigExpr)
return;
if (!isIteratorType(Call.getResultType()))
return;
auto State = C.getState();
// Already bound to container?
if (getIteratorPosition(State, Call.getReturnValue()))
return;
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
if (isEndCall(Func)) {
handleEnd(C, OrigExpr, Call.getReturnValue(),
InstCall->getCXXThisVal());
return;
}
}
// Copy-like and move constructors
if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
State = removeIteratorPosition(State, Call.getArgSVal(0));
}
C.addTransition(State);
return;
}
}
// Assumption: if return value is an iterator which is not yet bound to a
// container, then look for the first iterator argument, and
// bind the return value to the same container. This approach
// works for STL algorithms.
// FIXME: Add a more conservative mode
for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
if (isIteratorType(Call.getArgExpr(i)->getType())) {
if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
assignToContainer(C, OrigExpr, Call.getReturnValue(),
Pos->getContainer());
return;
}
}
}
}
}
void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
CheckerContext &C) const {
/* Transfer iterator state to temporary objects */
auto State = C.getState();
const auto *LCtx = C.getLocationContext();
const auto *Pos =
getIteratorPosition(State, State->getSVal(MTE->GetTemporaryExpr(), LCtx));
if (!Pos)
return;
State = setIteratorPosition(State, State->getSVal(MTE, LCtx), *Pos);
C.addTransition(State);
}
void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
CheckerContext &C) const {
// Cleanup
auto State = C.getState();
auto RegionMap = State->get<IteratorRegionMap>();
for (const auto Reg : RegionMap) {
if (!SR.isLiveRegion(Reg.first)) {
State = State->remove<IteratorRegionMap>(Reg.first);
}
}
auto SymbolMap = State->get<IteratorSymbolMap>();
for (const auto Sym : SymbolMap) {
if (!SR.isLive(Sym.first)) {
State = State->remove<IteratorSymbolMap>(Sym.first);
}
}
auto ContMap = State->get<ContainerMap>();
for (const auto Cont : ContMap) {
if (!SR.isLiveRegion(Cont.first)) {
State = State->remove<ContainerMap>(Cont.first);
}
}
auto ComparisonMap = State->get<IteratorComparisonMap>();
for (const auto Comp : ComparisonMap) {
if (!SR.isLive(Comp.first)) {
State = State->remove<IteratorComparisonMap>(Comp.first);
}
}
}
ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
bool Assumption) const {
// Load recorded comparison and transfer iterator state between sides
// according to comparison operator and assumption
const auto *SE = Cond.getAsSymExpr();
if (!SE)
return State;
auto Opc = getOpcode(SE);
if (Opc != BO_EQ && Opc != BO_NE)
return State;
bool Negated = false;
const auto *Comp = loadComparison(State, SE);
if (!Comp) {
// Try negated comparison, which is a SymExpr to 0 integer comparison
const auto *SIE = dyn_cast<SymIntExpr>(SE);
if (!SIE)
return State;
if (SIE->getRHS() != 0)
return State;
SE = SIE->getLHS();
Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
Opc = getOpcode(SE);
if (Opc != BO_EQ && Opc != BO_NE)
return State;
Comp = loadComparison(State, SE);
if (!Comp)
return State;
}
return processComparison(State, Comp->getLeft(), Comp->getRight(),
(Comp->isEquality() == Assumption) != Negated);
}
void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal,
const SVal &LVal, const SVal &RVal,
OverloadedOperatorKind Op) const {
// Record the operands and the operator of the comparison for the next
// evalAssume, if the result is a symbolic expression. If it is a concrete
// value (only one branch is possible), then transfer the state between
// the operands according to the operator and the result
auto State = C.getState();
if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
const auto *LPos = getIteratorPosition(State, LVal);
const auto *RPos = getIteratorPosition(State, RVal);
if (!LPos && !RPos)
return;
State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
C.addTransition(State);
} else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
if ((State = processComparison(
State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
(Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
C.addTransition(State);
} else {
C.generateSink(State, C.getPredecessor());
}
}
}
void IteratorChecker::verifyDereference(CheckerContext &C,
const SVal &Val) const {
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Val);
if (Pos && isOutOfRange(State, *Pos)) {
// If I do not put a tag here, some range tests will fail
static CheckerProgramPointTag Tag("IteratorRangeChecker",
"IteratorOutOfRange");
auto *N = C.generateNonFatalErrorNode(State, &Tag);
if (!N) {
return;
}
reportOutOfRangeBug("Iterator accessed outside of its range.", Val, C, N);
}
}
void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
const SVal &RetVal, const SVal &Cont) const {
const auto *ContReg = Cont.getAsRegion();
if (!ContReg)
return;
while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
ContReg = CBOR->getSuperRegion();
}
// If the container already has an end symbol then use it. Otherwise first
// create a new one.
auto State = C.getState();
auto EndSym = getContainerEnd(State, ContReg);
if (!EndSym) {
auto &SymMgr = C.getSymbolManager();
EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
C.getASTContext().LongTy, C.blockCount());
State = createContainerEnd(State, ContReg, EndSym);
}
State = setIteratorPosition(State, RetVal,
IteratorPosition::getPosition(ContReg, EndSym));
C.addTransition(State);
}
void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
const SVal &RetVal,
const MemRegion *Cont) const {
while (const auto *CBOR = Cont->getAs<CXXBaseObjectRegion>()) {
Cont = CBOR->getSuperRegion();
}
auto State = C.getState();
auto &SymMgr = C.getSymbolManager();
auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
C.getASTContext().LongTy, C.blockCount());
State = setIteratorPosition(State, RetVal,
IteratorPosition::getPosition(Cont, Sym));
C.addTransition(State);
}
void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
const SVal &Val, CheckerContext &C,
ExplodedNode *ErrNode) const {
auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
R->markInteresting(Val);
C.emitReport(std::move(R));
}
namespace {
bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
BinaryOperator::Opcode Opc);
bool isIteratorType(const QualType &Type) {
if (Type->isPointerType())
return true;
const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
return isIterator(CRD);
}
bool isIterator(const CXXRecordDecl *CRD) {
if (!CRD)
return false;
const auto Name = CRD->getName();
if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
Name.endswith_lower("it")))
return false;
bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
for (const auto *Method : CRD->methods()) {
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
if (Ctor->isCopyConstructor()) {
HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
}
continue;
}
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
continue;
}
if (Method->isCopyAssignmentOperator()) {
HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
continue;
}
if (!Method->isOverloadedOperator())
continue;
const auto OPK = Method->getOverloadedOperator();
if (OPK == OO_PlusPlus) {
HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
continue;
}
if (OPK == OO_Star) {
HasDerefOp = (Method->getNumParams() == 0);
continue;
}
}
return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
HasPostIncrOp && HasDerefOp;
}
bool isEndCall(const FunctionDecl *Func) {
const auto *IdInfo = Func->getIdentifier();
if (!IdInfo)
return false;
return IdInfo->getName().endswith_lower("end");
}
bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
}
bool isDereferenceOperator(OverloadedOperatorKind OK) {
return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
OK == OO_Subscript;
}
BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
return BSE->getOpcode();
} else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
const auto *COE = dyn_cast<CXXOperatorCallExpr>(SC->getStmt());
if (!COE)
return BO_Comma; // Extremal value, neither EQ nor NE
if (COE->getOperator() == OO_EqualEqual) {
return BO_EQ;
} else if (COE->getOperator() == OO_ExclaimEqual) {
return BO_NE;
}
return BO_Comma; // Extremal value, neither EQ nor NE
}
return BO_Comma; // Extremal value, neither EQ nor NE
}
const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
if (const auto Reg = Val.getAsRegion()) {
return Reg;
} else if (const auto Sym = Val.getAsSymbol()) {
return Sym;
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
return LCVal->getRegion();
}
return RegionOrSymbol();
}
const ProgramStateRef processComparison(ProgramStateRef State,
RegionOrSymbol LVal,
RegionOrSymbol RVal, bool Equal) {
const auto *LPos = getIteratorPosition(State, LVal);
const auto *RPos = getIteratorPosition(State, RVal);
if (LPos && !RPos) {
State = adjustIteratorPosition(State, RVal, *LPos, Equal);
} else if (!LPos && RPos) {
State = adjustIteratorPosition(State, LVal, *RPos, Equal);
} else if (LPos && RPos) {
State = relateIteratorPositions(State, *LPos, *RPos, Equal);
}
return State;
}
const ProgramStateRef saveComparison(ProgramStateRef State,
const SymExpr *Condition, const SVal &LVal,
const SVal &RVal, bool Eq) {
const auto Left = getRegionOrSymbol(LVal);
const auto Right = getRegionOrSymbol(RVal);
if (!Left || !Right)
return State;
return State->set<IteratorComparisonMap>(Condition,
IteratorComparison(Left, Right, Eq));
}
const IteratorComparison *loadComparison(ProgramStateRef State,
const SymExpr *Condition) {
return State->get<IteratorComparisonMap>(Condition);
}
SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
const auto *CDataPtr = getContainerData(State, Cont);
if (!CDataPtr)
return nullptr;
return CDataPtr->getEnd();
}
ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
const SymbolRef Sym) {
// Only create if it does not exist
const auto *CDataPtr = getContainerData(State, Cont);
if (CDataPtr) {
if (CDataPtr->getEnd()) {
return State;
} else {
const auto CData = CDataPtr->newEnd(Sym);
return setContainerData(State, Cont, CData);
}
} else {
const auto CData = ContainerData::fromEnd(Sym);
return setContainerData(State, Cont, CData);
}
}
const ContainerData *getContainerData(ProgramStateRef State,
const MemRegion *Cont) {
return State->get<ContainerMap>(Cont);
}
ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
const ContainerData &CData) {
return State->set<ContainerMap>(Cont, CData);
}
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
const SVal &Val) {
if (const auto Reg = Val.getAsRegion()) {
return State->get<IteratorRegionMap>(Reg);
} else if (const auto Sym = Val.getAsSymbol()) {
return State->get<IteratorSymbolMap>(Sym);
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
return State->get<IteratorRegionMap>(LCVal->getRegion());
}
return nullptr;
}
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
RegionOrSymbol RegOrSym) {
if (RegOrSym.is<const MemRegion *>()) {
return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
} else if (RegOrSym.is<SymbolRef>()) {
return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
}
return nullptr;
}
ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
const IteratorPosition &Pos) {
if (const auto Reg = Val.getAsRegion()) {
return State->set<IteratorRegionMap>(Reg, Pos);
} else if (const auto Sym = Val.getAsSymbol()) {
return State->set<IteratorSymbolMap>(Sym, Pos);
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
}
return nullptr;
}
ProgramStateRef setIteratorPosition(ProgramStateRef State,
RegionOrSymbol RegOrSym,
const IteratorPosition &Pos) {
if (RegOrSym.is<const MemRegion *>()) {
return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
Pos);
} else if (RegOrSym.is<SymbolRef>()) {
return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
}
return nullptr;
}
ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
if (const auto Reg = Val.getAsRegion()) {
return State->remove<IteratorRegionMap>(Reg);
} else if (const auto Sym = Val.getAsSymbol()) {
return State->remove<IteratorSymbolMap>(Sym);
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
return State->remove<IteratorRegionMap>(LCVal->getRegion());
}
return nullptr;
}
ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
RegionOrSymbol RegOrSym,
const IteratorPosition &Pos,
bool Equal) {
if (Equal) {
return setIteratorPosition(State, RegOrSym, Pos);
} else {
return State;
}
}
ProgramStateRef relateIteratorPositions(ProgramStateRef State,
const IteratorPosition &Pos1,
const IteratorPosition &Pos2,
bool Equal) {
// Try to compare them and get a defined value
auto &SVB = State->getStateManager().getSValBuilder();
const auto comparison =
SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()),
nonloc::SymbolVal(Pos2.getOffset()), SVB.getConditionType())
.getAs<DefinedSVal>();
if (comparison) {
return State->assume(*comparison, Equal);
}
return State;
}
bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
const auto *Cont = Pos.getContainer();
const auto *CData = getContainerData(State, Cont);
if (!CData)
return false;
// Out of range means less than the begin symbol or greater or equal to the
// end symbol.
const auto End = CData->getEnd();
if (End) {
if (isGreaterOrEqual(State, Pos.getOffset(), End)) {
return true;
}
}
return false;
}
bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
return compare(State, Sym1, Sym2, BO_GE);
}
bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
BinaryOperator::Opcode Opc) {
auto &SMgr = State->getStateManager();
auto &SVB = SMgr.getSValBuilder();
const auto comparison =
SVB.evalBinOp(State, Opc, nonloc::SymbolVal(Sym1),
nonloc::SymbolVal(Sym2), SVB.getConditionType())
.getAs<DefinedSVal>();
if(comparison) {
return !!State->assume(*comparison, true);
}
return false;
}
} // namespace
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &Mgr) { \
auto *checker = Mgr.registerChecker<IteratorChecker>(); \
checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
checker->CheckNames[IteratorChecker::CK_##name] = \
Mgr.getCurrentCheckName(); \
}
REGISTER_CHECKER(IteratorRangeChecker)

View File

@ -1,840 +0,0 @@
//===-- IteratorPastEndChecker.cpp --------------------------------*- C++ -*--//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Defines a checker for using iterators outside their range (past end). Usage
// means here dereferencing, incrementing etc.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include <utility>
using namespace clang;
using namespace ento;
namespace {
struct IteratorPosition {
private:
enum Kind { InRange, OutofRange } K;
IteratorPosition(Kind InK) : K(InK) {}
public:
bool isInRange() const { return K == InRange; }
bool isOutofRange() const { return K == OutofRange; }
static IteratorPosition getInRange() { return IteratorPosition(InRange); }
static IteratorPosition getOutofRange() {
return IteratorPosition(OutofRange);
}
bool operator==(const IteratorPosition &X) const { return K == X.K; }
bool operator!=(const IteratorPosition &X) const { return K != X.K; }
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
};
typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
struct IteratorComparison {
private:
RegionOrSymbol Left, Right;
bool Equality;
public:
IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
: Left(L), Right(R), Equality(Eq) {}
RegionOrSymbol getLeft() const { return Left; }
RegionOrSymbol getRight() const { return Right; }
bool isEquality() const { return Equality; }
bool operator==(const IteratorComparison &X) const {
return Left == X.Left && Right == X.Right && Equality == X.Equality;
}
bool operator!=(const IteratorComparison &X) const {
return Left != X.Left || Right != X.Right || Equality != X.Equality;
}
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
};
class IteratorPastEndChecker
: public Checker<
check::PreCall, check::PostCall, check::PreStmt<CXXOperatorCallExpr>,
check::PostStmt<CXXConstructExpr>, check::PostStmt<DeclStmt>,
check::PostStmt<MaterializeTemporaryExpr>, check::BeginFunction,
check::DeadSymbols, eval::Assume, eval::Call> {
mutable IdentifierInfo *II_find = nullptr,
*II_find_end = nullptr, *II_find_first_of = nullptr,
*II_find_if = nullptr, *II_find_if_not = nullptr,
*II_lower_bound = nullptr, *II_upper_bound = nullptr,
*II_search = nullptr, *II_search_n = nullptr;
std::unique_ptr<BugType> PastEndBugType;
void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
const SVal &RVal, OverloadedOperatorKind Op) const;
void handleAccess(CheckerContext &C, const SVal &Val) const;
void handleDecrement(CheckerContext &C, const SVal &Val) const;
void handleEnd(CheckerContext &C, const SVal &RetVal) const;
bool evalFind(CheckerContext &C, const CallExpr *CE) const;
bool evalFindEnd(CheckerContext &C, const CallExpr *CE) const;
bool evalFindFirstOf(CheckerContext &C, const CallExpr *CE) const;
bool evalFindIf(CheckerContext &C, const CallExpr *CE) const;
bool evalFindIfNot(CheckerContext &C, const CallExpr *CE) const;
bool evalLowerBound(CheckerContext &C, const CallExpr *CE) const;
bool evalUpperBound(CheckerContext &C, const CallExpr *CE) const;
bool evalSearch(CheckerContext &C, const CallExpr *CE) const;
bool evalSearchN(CheckerContext &C, const CallExpr *CE) const;
void Find(CheckerContext &C, const CallExpr *CE) const;
void reportPastEndBug(const StringRef &Message, const SVal &Val,
CheckerContext &C, ExplodedNode *ErrNode) const;
void initIdentifiers(ASTContext &Ctx) const;
public:
IteratorPastEndChecker();
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreStmt(const CXXOperatorCallExpr *COCE, CheckerContext &C) const;
void checkBeginFunction(CheckerContext &C) const;
void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
void checkPostStmt(const MaterializeTemporaryExpr *MTE,
CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
bool Assumption) const;
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
};
}
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
IteratorPosition)
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
IteratorComparison)
#define INIT_ID(Id) \
if (!II_##Id) \
II_##Id = &Ctx.Idents.get(#Id)
namespace {
bool isIteratorType(const QualType &Type);
bool isIterator(const CXXRecordDecl *CRD);
bool isEndCall(const FunctionDecl *Func);
bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
bool isAccessOperator(OverloadedOperatorKind OK);
bool isDecrementOperator(OverloadedOperatorKind OK);
BinaryOperator::Opcode getOpcode(const SymExpr *SE);
const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
const ProgramStateRef processComparison(ProgramStateRef State,
RegionOrSymbol LVal,
RegionOrSymbol RVal, bool Equal);
const ProgramStateRef saveComparison(ProgramStateRef State,
const SymExpr *Condition, const SVal &LVal,
const SVal &RVal, bool Eq);
const IteratorComparison *loadComparison(ProgramStateRef State,
const SymExpr *Condition);
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
const SVal &Val);
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
RegionOrSymbol RegOrSym);
ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
IteratorPosition Pos);
ProgramStateRef setIteratorPosition(ProgramStateRef State,
RegionOrSymbol RegOrSym,
IteratorPosition Pos);
ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
RegionOrSymbol RegOrSym,
IteratorPosition Pos, bool Equal);
bool contradictingIteratorPositions(IteratorPosition Pos1,
IteratorPosition Pos2, bool Equal);
}
IteratorPastEndChecker::IteratorPastEndChecker() {
PastEndBugType.reset(
new BugType(this, "Iterator Past End", "Misuse of STL APIs"));
PastEndBugType->setSuppressOnSink(true);
}
void IteratorPastEndChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
// Check for access past end
const auto *Func = Call.getDecl()->getAsFunction();
if (!Func)
return;
if (Func->isOverloadedOperator()) {
if (isAccessOperator(Func->getOverloadedOperator())) {
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
handleAccess(C, InstCall->getCXXThisVal());
} else {
handleAccess(C, Call.getArgSVal(0));
}
}
}
}
void IteratorPastEndChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
// Record end() iterators, iterator decrementation and comparison
const auto *Func = Call.getDecl()->getAsFunction();
if (!Func)
return;
if (Func->isOverloadedOperator()) {
const auto Op = Func->getOverloadedOperator();
if (isSimpleComparisonOperator(Op)) {
if (Func->isCXXInstanceMember()) {
const auto &InstCall = static_cast<const CXXInstanceCall &>(Call);
handleComparison(C, InstCall.getReturnValue(), InstCall.getCXXThisVal(),
InstCall.getArgSVal(0), Op);
} else {
handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
Call.getArgSVal(1), Op);
}
} else if (isDecrementOperator(Func->getOverloadedOperator())) {
if (Func->isCXXInstanceMember()) {
const auto &InstCall = static_cast<const CXXInstanceCall &>(Call);
handleDecrement(C, InstCall.getCXXThisVal());
} else {
handleDecrement(C, Call.getArgSVal(0));
}
}
} else if (Func->isCXXInstanceMember()) {
if (!isEndCall(Func))
return;
if (!isIteratorType(Call.getResultType()))
return;
handleEnd(C, Call.getReturnValue());
}
}
void IteratorPastEndChecker::checkPreStmt(const CXXOperatorCallExpr *COCE,
CheckerContext &C) const {
const auto *ThisExpr = COCE->getArg(0);
auto State = C.getState();
const auto *LCtx = C.getPredecessor()->getLocationContext();
const auto CurrentThis = State->getSVal(ThisExpr, LCtx);
if (const auto *Reg = CurrentThis.getAsRegion()) {
if (!Reg->getAs<CXXTempObjectRegion>())
return;
const auto OldState = C.getPredecessor()->getFirstPred()->getState();
const auto OldThis = OldState->getSVal(ThisExpr, LCtx);
const auto *Pos = getIteratorPosition(OldState, OldThis);
if (!Pos)
return;
State = setIteratorPosition(State, CurrentThis, *Pos);
C.addTransition(State);
}
}
void IteratorPastEndChecker::checkBeginFunction(CheckerContext &C) const {
// Copy state of iterator arguments to iterator parameters
auto State = C.getState();
const auto *LCtx = C.getLocationContext();
const auto *Site = cast<StackFrameContext>(LCtx)->getCallSite();
if (!Site)
return;
const auto *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
if (!FD)
return;
const auto *CE = dyn_cast<CallExpr>(Site);
if (!CE)
return;
bool Change = false;
int idx = 0;
for (const auto P : FD->parameters()) {
auto Param = State->getLValue(P, LCtx);
auto Arg = State->getSVal(CE->getArg(idx++), LCtx->getParent());
const auto *Pos = getIteratorPosition(State, Arg);
if (!Pos)
continue;
State = setIteratorPosition(State, Param, *Pos);
Change = true;
}
if (Change) {
C.addTransition(State);
}
}
void IteratorPastEndChecker::checkPostStmt(const CXXConstructExpr *CCE,
CheckerContext &C) const {
// Transfer iterator state in case of copy or move by constructor
const auto *ctr = CCE->getConstructor();
if (!ctr->isCopyOrMoveConstructor())
return;
const auto *RHSExpr = CCE->getArg(0);
auto State = C.getState();
const auto *LCtx = C.getLocationContext();
const auto RetVal = State->getSVal(CCE, LCtx);
const auto RHSVal = State->getSVal(RHSExpr, LCtx);
const auto *RHSPos = getIteratorPosition(State, RHSVal);
if (!RHSPos)
return;
State = setIteratorPosition(State, RetVal, *RHSPos);
C.addTransition(State);
}
void IteratorPastEndChecker::checkPostStmt(const DeclStmt *DS,
CheckerContext &C) const {
// Transfer iterator state to new variable declaration
for (const auto *D : DS->decls()) {
const auto *VD = dyn_cast<VarDecl>(D);
if (!VD || !VD->hasInit())
continue;
auto State = C.getState();
const auto *LCtx = C.getPredecessor()->getLocationContext();
const auto *Pos =
getIteratorPosition(State, State->getSVal(VD->getInit(), LCtx));
if (!Pos)
continue;
State = setIteratorPosition(State, State->getLValue(VD, LCtx), *Pos);
C.addTransition(State);
}
}
void IteratorPastEndChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
CheckerContext &C) const {
/* Transfer iterator state for to temporary objects */
auto State = C.getState();
const auto *LCtx = C.getPredecessor()->getLocationContext();
const auto *Pos =
getIteratorPosition(State, State->getSVal(MTE->GetTemporaryExpr(), LCtx));
if (!Pos)
return;
State = setIteratorPosition(State, State->getSVal(MTE, LCtx), *Pos);
C.addTransition(State);
}
void IteratorPastEndChecker::checkDeadSymbols(SymbolReaper &SR,
CheckerContext &C) const {
auto State = C.getState();
auto RegionMap = State->get<IteratorRegionMap>();
for (const auto Reg : RegionMap) {
if (!SR.isLiveRegion(Reg.first)) {
State = State->remove<IteratorRegionMap>(Reg.first);
}
}
auto SymbolMap = State->get<IteratorSymbolMap>();
for (const auto Sym : SymbolMap) {
if (SR.isDead(Sym.first)) {
State = State->remove<IteratorSymbolMap>(Sym.first);
}
}
auto ComparisonMap = State->get<IteratorComparisonMap>();
for (const auto Comp : ComparisonMap) {
if (SR.isDead(Comp.first)) {
State = State->remove<IteratorComparisonMap>(Comp.first);
}
}
}
ProgramStateRef IteratorPastEndChecker::evalAssume(ProgramStateRef State,
SVal Cond,
bool Assumption) const {
// Load recorded comparison and transfer iterator state between sides
// according to comparison operator and assumption
const auto *SE = Cond.getAsSymExpr();
if (!SE)
return State;
auto Opc = getOpcode(SE);
if (Opc != BO_EQ && Opc != BO_NE)
return State;
bool Negated = false;
const auto *Comp = loadComparison(State, SE);
if (!Comp) {
// Try negated comparison, which is a SymExpr to 0 integer comparison
const auto *SIE = dyn_cast<SymIntExpr>(SE);
if (!SIE)
return State;
if (SIE->getRHS() != 0)
return State;
SE = SIE->getLHS();
Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
Opc = getOpcode(SE);
if (Opc != BO_EQ && Opc != BO_NE)
return State;
Comp = loadComparison(State, SE);
if (!Comp)
return State;
}
return processComparison(State, Comp->getLeft(), Comp->getRight(),
(Comp->isEquality() == Assumption) != Negated);
}
// FIXME: Evaluation of these STL calls should be moved to StdCLibraryFunctions
// checker (see patch r284960) or another similar checker for C++ STL
// functions (e.g. StdCXXLibraryFunctions or StdCppLibraryFunctions).
bool IteratorPastEndChecker::evalCall(const CallExpr *CE,
CheckerContext &C) const {
const FunctionDecl *FD = C.getCalleeDecl(CE);
if (!FD)
return false;
ASTContext &Ctx = C.getASTContext();
initIdentifiers(Ctx);
if (FD->getKind() == Decl::Function) {
if (FD->isInStdNamespace()) {
if (FD->getIdentifier() == II_find) {
return evalFind(C, CE);
} else if (FD->getIdentifier() == II_find_end) {
return evalFindEnd(C, CE);
} else if (FD->getIdentifier() == II_find_first_of) {
return evalFindFirstOf(C, CE);
} else if (FD->getIdentifier() == II_find_if) {
return evalFindIf(C, CE);
} else if (FD->getIdentifier() == II_find_if_not) {
return evalFindIfNot(C, CE);
} else if (FD->getIdentifier() == II_upper_bound) {
return evalUpperBound(C, CE);
} else if (FD->getIdentifier() == II_lower_bound) {
return evalLowerBound(C, CE);
} else if (FD->getIdentifier() == II_search) {
return evalSearch(C, CE);
} else if (FD->getIdentifier() == II_search_n) {
return evalSearchN(C, CE);
}
}
}
return false;
}
void IteratorPastEndChecker::handleComparison(CheckerContext &C,
const SVal &RetVal,
const SVal &LVal,
const SVal &RVal,
OverloadedOperatorKind Op) const {
// Record the operands and the operator of the comparison for the next
// evalAssume, if the result is a symbolic expression. If it is a concrete
// value (only one branch is possible), then transfer the state between
// the operands according to the operator and the result
auto State = C.getState();
if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
const auto *LPos = getIteratorPosition(State, LVal);
const auto *RPos = getIteratorPosition(State, RVal);
if (!LPos && !RPos)
return;
State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
C.addTransition(State);
} else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
if ((State = processComparison(
State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
(Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
C.addTransition(State);
} else {
C.generateSink(State, C.getPredecessor());
}
}
}
void IteratorPastEndChecker::handleAccess(CheckerContext &C,
const SVal &Val) const {
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Val);
if (Pos && Pos->isOutofRange()) {
auto *N = C.generateNonFatalErrorNode(State);
if (!N) {
return;
}
reportPastEndBug("Iterator accessed past its end.", Val, C, N);
}
}
void IteratorPastEndChecker::handleDecrement(CheckerContext &C,
const SVal &Val) const {
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Val);
if (Pos && Pos->isOutofRange()) {
State = setIteratorPosition(State, Val, IteratorPosition::getInRange());
// FIXME: We could also check for iterators ahead of their beginnig in the
// future, but currently we do not care for such errors. We also
// assume that the iterator is not past its end by more then one
// position.
C.addTransition(State);
}
}
void IteratorPastEndChecker::handleEnd(CheckerContext &C,
const SVal &RetVal) const {
auto State = C.getState();
State = setIteratorPosition(State, RetVal, IteratorPosition::getOutofRange());
C.addTransition(State);
}
bool IteratorPastEndChecker::evalFind(CheckerContext &C,
const CallExpr *CE) const {
if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
isIteratorType(CE->getArg(1)->getType())) {
Find(C, CE);
return true;
}
return false;
}
bool IteratorPastEndChecker::evalFindEnd(CheckerContext &C,
const CallExpr *CE) const {
if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
isIteratorType(CE->getArg(0)->getType()) &&
isIteratorType(CE->getArg(1)->getType()) &&
isIteratorType(CE->getArg(2)->getType()) &&
isIteratorType(CE->getArg(3)->getType())) {
Find(C, CE);
return true;
}
return false;
}
bool IteratorPastEndChecker::evalFindFirstOf(CheckerContext &C,
const CallExpr *CE) const {
if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
isIteratorType(CE->getArg(0)->getType()) &&
isIteratorType(CE->getArg(1)->getType()) &&
isIteratorType(CE->getArg(2)->getType()) &&
isIteratorType(CE->getArg(3)->getType())) {
Find(C, CE);
return true;
}
return false;
}
bool IteratorPastEndChecker::evalFindIf(CheckerContext &C,
const CallExpr *CE) const {
if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
isIteratorType(CE->getArg(1)->getType())) {
Find(C, CE);
return true;
}
return false;
}
bool IteratorPastEndChecker::evalFindIfNot(CheckerContext &C,
const CallExpr *CE) const {
if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
isIteratorType(CE->getArg(1)->getType())) {
Find(C, CE);
return true;
}
return false;
}
bool IteratorPastEndChecker::evalLowerBound(CheckerContext &C,
const CallExpr *CE) const {
if ((CE->getNumArgs() == 3 || CE->getNumArgs() == 4) &&
isIteratorType(CE->getArg(0)->getType()) &&
isIteratorType(CE->getArg(1)->getType())) {
Find(C, CE);
return true;
}
return false;
}
bool IteratorPastEndChecker::evalUpperBound(CheckerContext &C,
const CallExpr *CE) const {
if ((CE->getNumArgs() == 3 || CE->getNumArgs() == 4) &&
isIteratorType(CE->getArg(0)->getType()) &&
isIteratorType(CE->getArg(1)->getType())) {
Find(C, CE);
return true;
}
return false;
}
bool IteratorPastEndChecker::evalSearch(CheckerContext &C,
const CallExpr *CE) const {
if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
isIteratorType(CE->getArg(0)->getType()) &&
isIteratorType(CE->getArg(1)->getType()) &&
isIteratorType(CE->getArg(2)->getType()) &&
isIteratorType(CE->getArg(3)->getType())) {
Find(C, CE);
return true;
}
return false;
}
bool IteratorPastEndChecker::evalSearchN(CheckerContext &C,
const CallExpr *CE) const {
if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
isIteratorType(CE->getArg(0)->getType()) &&
isIteratorType(CE->getArg(1)->getType())) {
Find(C, CE);
return true;
}
return false;
}
void IteratorPastEndChecker::Find(CheckerContext &C, const CallExpr *CE) const {
auto state = C.getState();
auto &svalBuilder = C.getSValBuilder();
const auto *LCtx = C.getLocationContext();
auto RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
auto SecondParam = state->getSVal(CE->getArg(1), LCtx);
auto stateFound = state->BindExpr(CE, LCtx, RetVal);
auto stateNotFound = state->BindExpr(CE, LCtx, SecondParam);
C.addTransition(stateFound);
C.addTransition(stateNotFound);
}
void IteratorPastEndChecker::reportPastEndBug(const StringRef &Message,
const SVal &Val,
CheckerContext &C,
ExplodedNode *ErrNode) const {
auto R = llvm::make_unique<BugReport>(*PastEndBugType, Message, ErrNode);
R->markInteresting(Val);
C.emitReport(std::move(R));
}
void IteratorPastEndChecker::initIdentifiers(ASTContext &Ctx) const {
INIT_ID(find);
INIT_ID(find_end);
INIT_ID(find_first_of);
INIT_ID(find_if);
INIT_ID(find_if_not);
INIT_ID(lower_bound);
INIT_ID(upper_bound);
INIT_ID(search);
INIT_ID(search_n);
}
namespace {
bool isIteratorType(const QualType &Type) {
if (Type->isPointerType())
return true;
const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
return isIterator(CRD);
}
bool isIterator(const CXXRecordDecl *CRD) {
if (!CRD)
return false;
const auto Name = CRD->getName();
if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
Name.endswith_lower("it")))
return false;
bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
for (const auto *Method : CRD->methods()) {
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
if (Ctor->isCopyConstructor()) {
HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
}
continue;
}
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
continue;
}
if (Method->isCopyAssignmentOperator()) {
HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
continue;
}
if (!Method->isOverloadedOperator())
continue;
const auto OPK = Method->getOverloadedOperator();
if (OPK == OO_PlusPlus) {
HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
continue;
}
if (OPK == OO_Star) {
HasDerefOp = (Method->getNumParams() == 0);
continue;
}
}
return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
HasPostIncrOp && HasDerefOp;
}
bool isEndCall(const FunctionDecl *Func) {
const auto *IdInfo = Func->getIdentifier();
if (!IdInfo)
return false;
return IdInfo->getName().endswith_lower("end");
}
bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
}
bool isAccessOperator(OverloadedOperatorKind OK) {
return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
OK == OO_Plus || OK == OO_PlusEqual || OK == OO_PlusPlus ||
OK == OO_Subscript;
}
bool isDecrementOperator(OverloadedOperatorKind OK) {
return OK == OO_MinusEqual || OK == OO_MinusMinus;
}
BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
return BSE->getOpcode();
} else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
const auto *COE = dyn_cast<CXXOperatorCallExpr>(SC->getStmt());
if (!COE)
return BO_Comma; // Extremal value, neither EQ nor NE
if (COE->getOperator() == OO_EqualEqual) {
return BO_EQ;
} else if (COE->getOperator() == OO_ExclaimEqual) {
return BO_NE;
}
return BO_Comma; // Extremal value, neither EQ nor NE
}
return BO_Comma; // Extremal value, neither EQ nor NE
}
const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
if (const auto Reg = Val.getAsRegion()) {
return Reg;
} else if (const auto Sym = Val.getAsSymbol()) {
return Sym;
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
return LCVal->getRegion();
}
return RegionOrSymbol();
}
const ProgramStateRef processComparison(ProgramStateRef State,
RegionOrSymbol LVal,
RegionOrSymbol RVal, bool Equal) {
const auto *LPos = getIteratorPosition(State, LVal);
const auto *RPos = getIteratorPosition(State, RVal);
if (LPos && !RPos) {
State = adjustIteratorPosition(State, RVal, *LPos, Equal);
} else if (!LPos && RPos) {
State = adjustIteratorPosition(State, LVal, *RPos, Equal);
} else if (LPos && RPos) {
if (contradictingIteratorPositions(*LPos, *RPos, Equal)) {
return nullptr;
}
}
return State;
}
const ProgramStateRef saveComparison(ProgramStateRef State,
const SymExpr *Condition, const SVal &LVal,
const SVal &RVal, bool Eq) {
const auto Left = getRegionOrSymbol(LVal);
const auto Right = getRegionOrSymbol(RVal);
if (!Left || !Right)
return State;
return State->set<IteratorComparisonMap>(Condition,
IteratorComparison(Left, Right, Eq));
}
const IteratorComparison *loadComparison(ProgramStateRef State,
const SymExpr *Condition) {
return State->get<IteratorComparisonMap>(Condition);
}
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
const SVal &Val) {
if (const auto Reg = Val.getAsRegion()) {
return State->get<IteratorRegionMap>(Reg);
} else if (const auto Sym = Val.getAsSymbol()) {
return State->get<IteratorSymbolMap>(Sym);
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
return State->get<IteratorRegionMap>(LCVal->getRegion());
}
return nullptr;
}
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
RegionOrSymbol RegOrSym) {
if (RegOrSym.is<const MemRegion *>()) {
return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
} else if (RegOrSym.is<SymbolRef>()) {
return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
}
return nullptr;
}
ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
IteratorPosition Pos) {
if (const auto Reg = Val.getAsRegion()) {
return State->set<IteratorRegionMap>(Reg, Pos);
} else if (const auto Sym = Val.getAsSymbol()) {
return State->set<IteratorSymbolMap>(Sym, Pos);
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
}
return nullptr;
}
ProgramStateRef setIteratorPosition(ProgramStateRef State,
RegionOrSymbol RegOrSym,
IteratorPosition Pos) {
if (RegOrSym.is<const MemRegion *>()) {
return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
Pos);
} else if (RegOrSym.is<SymbolRef>()) {
return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
}
return nullptr;
}
ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
RegionOrSymbol RegOrSym,
IteratorPosition Pos, bool Equal) {
if ((Pos.isInRange() && Equal) || (Pos.isOutofRange() && !Equal)) {
return setIteratorPosition(State, RegOrSym, IteratorPosition::getInRange());
} else if (Pos.isOutofRange() && Equal) {
return setIteratorPosition(State, RegOrSym,
IteratorPosition::getOutofRange());
} else {
return State;
}
}
bool contradictingIteratorPositions(IteratorPosition Pos1,
IteratorPosition Pos2, bool Equal) {
return ((Pos1 != Pos2) && Equal) ||
((Pos1.isOutofRange() && Pos2.isOutofRange()) && !Equal);
}
}
void ento::registerIteratorPastEndChecker(CheckerManager &Mgr) {
Mgr.registerChecker<IteratorPastEndChecker>();
}

View File

@ -25,7 +25,13 @@ using namespace ento;
namespace {
struct LockState {
enum Kind { Destroyed, Locked, Unlocked } K;
enum Kind {
Destroyed,
Locked,
Unlocked,
UntouchedAndPossiblyDestroyed,
UnlockedAndPossiblyDestroyed
} K;
private:
LockState(Kind K) : K(K) {}
@ -34,6 +40,12 @@ struct LockState {
static LockState getLocked() { return LockState(Locked); }
static LockState getUnlocked() { return LockState(Unlocked); }
static LockState getDestroyed() { return LockState(Destroyed); }
static LockState getUntouchedAndPossiblyDestroyed() {
return LockState(UntouchedAndPossiblyDestroyed);
}
static LockState getUnlockedAndPossiblyDestroyed() {
return LockState(UnlockedAndPossiblyDestroyed);
}
bool operator==(const LockState &X) const {
return K == X.K;
@ -42,13 +54,20 @@ struct LockState {
bool isLocked() const { return K == Locked; }
bool isUnlocked() const { return K == Unlocked; }
bool isDestroyed() const { return K == Destroyed; }
bool isUntouchedAndPossiblyDestroyed() const {
return K == UntouchedAndPossiblyDestroyed;
}
bool isUnlockedAndPossiblyDestroyed() const {
return K == UnlockedAndPossiblyDestroyed;
}
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(K);
}
};
class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > {
class PthreadLockChecker
: public Checker<check::PostStmt<CallExpr>, check::DeadSymbols> {
mutable std::unique_ptr<BugType> BT_doublelock;
mutable std::unique_ptr<BugType> BT_doubleunlock;
mutable std::unique_ptr<BugType> BT_destroylock;
@ -61,22 +80,31 @@ class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > {
};
public:
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
bool isTryLock, enum LockingSemantics semantics) const;
void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock,
enum LockingSemantics semantics) const;
void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const;
ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
const MemRegion *lockR,
const SymbolRef *sym) const;
};
} // end anonymous namespace
// GDM Entry for tracking lock state.
// A stack of locks for tracking lock-unlock order.
REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
// An entry for tracking lock states.
REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
// Return values for unresolved calls to pthread_mutex_destroy().
REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef)
void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
CheckerContext &C) const {
ProgramStateRef state = C.getState();
@ -113,13 +141,49 @@ void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
FName == "lck_mtx_unlock" ||
FName == "lck_rw_done")
ReleaseLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
else if (FName == "pthread_mutex_destroy" ||
FName == "lck_mtx_destroy")
DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
else if (FName == "pthread_mutex_destroy")
DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx), PthreadSemantics);
else if (FName == "lck_mtx_destroy")
DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx), XNUSemantics);
else if (FName == "pthread_mutex_init")
InitLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
}
// When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
// sure if the destroy call has succeeded or failed, and the lock enters one of
// the 'possibly destroyed' state. There is a short time frame for the
// programmer to check the return value to see if the lock was successfully
// destroyed. Before we model the next operation over that lock, we call this
// function to see if the return value was checked by now and set the lock state
// - either to destroyed state or back to its previous state.
// In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
// successfully destroyed and it returns a non-zero value otherwise.
ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex(
ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const {
const LockState *lstate = state->get<LockMap>(lockR);
// Existence in DestroyRetVal ensures existence in LockMap.
// Existence in Destroyed also ensures that the lock state for lockR is either
// UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
assert(lstate->isUntouchedAndPossiblyDestroyed() ||
lstate->isUnlockedAndPossiblyDestroyed());
ConstraintManager &CMgr = state->getConstraintManager();
ConditionTruthVal retZero = CMgr.isNull(state, *sym);
if (retZero.isConstrainedFalse()) {
if (lstate->isUntouchedAndPossiblyDestroyed())
state = state->remove<LockMap>(lockR);
else if (lstate->isUnlockedAndPossiblyDestroyed())
state = state->set<LockMap>(lockR, LockState::getUnlocked());
} else
state = state->set<LockMap>(lockR, LockState::getDestroyed());
// Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is
// now resolved.
state = state->remove<DestroyRetVal>(lockR);
return state;
}
void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
SVal lock, bool isTryLock,
enum LockingSemantics semantics) const {
@ -129,6 +193,9 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
return;
ProgramStateRef state = C.getState();
const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
if (sym)
state = resolvePossiblyDestroyedMutex(state, lockR, sym);
SVal X = state->getSVal(CE, C.getLocationContext());
if (X.isUnknownOrUndef())
@ -197,6 +264,9 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
return;
ProgramStateRef state = C.getState();
const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
if (sym)
state = resolvePossiblyDestroyedMutex(state, lockR, sym);
if (const LockState *LState = state->get<LockMap>(lockR)) {
if (LState->isUnlocked()) {
@ -245,7 +315,8 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
}
void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
SVal Lock) const {
SVal Lock,
enum LockingSemantics semantics) const {
const MemRegion *LockR = Lock.getAsRegion();
if (!LockR)
@ -253,13 +324,38 @@ void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
ProgramStateRef State = C.getState();
const LockState *LState = State->get<LockMap>(LockR);
if (!LState || LState->isUnlocked()) {
State = State->set<LockMap>(LockR, LockState::getDestroyed());
C.addTransition(State);
return;
}
const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
if (sym)
State = resolvePossiblyDestroyedMutex(State, LockR, sym);
const LockState *LState = State->get<LockMap>(LockR);
// Checking the return value of the destroy method only in the case of
// PthreadSemantics
if (semantics == PthreadSemantics) {
if (!LState || LState->isUnlocked()) {
SymbolRef sym = C.getSVal(CE).getAsSymbol();
if (!sym) {
State = State->remove<LockMap>(LockR);
C.addTransition(State);
return;
}
State = State->set<DestroyRetVal>(LockR, sym);
if (LState && LState->isUnlocked())
State = State->set<LockMap>(
LockR, LockState::getUnlockedAndPossiblyDestroyed());
else
State = State->set<LockMap>(
LockR, LockState::getUntouchedAndPossiblyDestroyed());
C.addTransition(State);
return;
}
} else {
if (!LState || LState->isUnlocked()) {
State = State->set<LockMap>(LockR, LockState::getDestroyed());
C.addTransition(State);
return;
}
}
StringRef Message;
if (LState->isLocked()) {
@ -288,6 +384,10 @@ void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
ProgramStateRef State = C.getState();
const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
if (sym)
State = resolvePossiblyDestroyedMutex(State, LockR, sym);
const struct LockState *LState = State->get<LockMap>(LockR);
if (!LState || LState->isDestroyed()) {
State = State->set<LockMap>(LockR, LockState::getUnlocked());
@ -328,6 +428,26 @@ void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
C.emitReport(std::move(Report));
}
void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
// TODO: Clean LockMap when a mutex region dies.
DestroyRetValTy TrackedSymbols = State->get<DestroyRetVal>();
for (DestroyRetValTy::iterator I = TrackedSymbols.begin(),
E = TrackedSymbols.end();
I != E; ++I) {
const SymbolRef Sym = I->second;
const MemRegion *lockR = I->first;
bool IsSymDead = SymReaper.isDead(Sym);
// Remove the dead symbol from the return value symbols map.
if (IsSymDead)
State = resolvePossiblyDestroyedMutex(State, lockR, &Sym);
}
C.addTransition(State);
}
void ento::registerPthreadLockChecker(CheckerManager &mgr) {
mgr.registerChecker<PthreadLockChecker>();
}

View File

@ -644,15 +644,33 @@ ProgramStateRef ProgramState::addTaint(const Stmt *S,
if (const Expr *E = dyn_cast_or_null<Expr>(S))
S = E->IgnoreParens();
SymbolRef Sym = getSVal(S, LCtx).getAsSymbol();
return addTaint(getSVal(S, LCtx), Kind);
}
ProgramStateRef ProgramState::addTaint(SVal V,
TaintTagType Kind) const {
SymbolRef Sym = V.getAsSymbol();
if (Sym)
return addTaint(Sym, Kind);
const MemRegion *R = getSVal(S, LCtx).getAsRegion();
addTaint(R, Kind);
// If the SVal represents a structure, try to mass-taint all values within the
// structure. For now it only works efficiently on lazy compound values that
// were conjured during a conservative evaluation of a function - either as
// return values of functions that return structures or arrays by value, or as
// values of structures or arrays passed into the function by reference,
// directly or through pointer aliasing. Such lazy compound values are
// characterized by having exactly one binding in their captured store within
// their parent region, which is a conjured symbol default-bound to the base
// region of the parent region.
if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) {
if (Optional<SVal> binding = getStateManager().StoreMgr->getDefaultBinding(*LCV)) {
if (SymbolRef Sym = binding->getAsSymbol())
return addPartialTaint(Sym, LCV->getRegion(), Kind);
}
}
// Cannot add taint, so just return the state.
return this;
const MemRegion *R = V.getAsRegion();
return addTaint(R, Kind);
}
ProgramStateRef ProgramState::addTaint(const MemRegion *R,
@ -674,6 +692,27 @@ ProgramStateRef ProgramState::addTaint(SymbolRef Sym,
return NewState;
}
ProgramStateRef ProgramState::addPartialTaint(SymbolRef ParentSym,
const SubRegion *SubRegion,
TaintTagType Kind) const {
// Ignore partial taint if the entire parent symbol is already tainted.
if (contains<TaintMap>(ParentSym) && *get<TaintMap>(ParentSym) == Kind)
return this;
// Partial taint applies if only a portion of the symbol is tainted.
if (SubRegion == SubRegion->getBaseRegion())
return addTaint(ParentSym, Kind);
const TaintedSubRegions *SavedRegs = get<DerivedSymTaint>(ParentSym);
TaintedSubRegions Regs =
SavedRegs ? *SavedRegs : stateMgr->TSRFactory.getEmptyMap();
Regs = stateMgr->TSRFactory.add(Regs, SubRegion, Kind);
ProgramStateRef NewState = set<DerivedSymTaint>(ParentSym, Regs);
assert(NewState);
return NewState;
}
bool ProgramState::isTainted(const Stmt *S, const LocationContext *LCtx,
TaintTagType Kind) const {
if (const Expr *E = dyn_cast_or_null<Expr>(S))
@ -714,31 +753,52 @@ bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const {
return false;
// Traverse all the symbols this symbol depends on to see if any are tainted.
bool Tainted = false;
for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), SE =Sym->symbol_end();
SI != SE; ++SI) {
if (!isa<SymbolData>(*SI))
continue;
const TaintTagType *Tag = get<TaintMap>(*SI);
Tainted = (Tag && *Tag == Kind);
if (const TaintTagType *Tag = get<TaintMap>(*SI)) {
if (*Tag == Kind)
return true;
}
// If this is a SymbolDerived with a tainted parent, it's also tainted.
if (const SymbolDerived *SD = dyn_cast<SymbolDerived>(*SI))
Tainted = Tainted || isTainted(SD->getParentSymbol(), Kind);
if (const SymbolDerived *SD = dyn_cast<SymbolDerived>(*SI)) {
// If this is a SymbolDerived with a tainted parent, it's also tainted.
if (isTainted(SD->getParentSymbol(), Kind))
return true;
// If this is a SymbolDerived with the same parent symbol as another
// tainted SymbolDerived and a region that's a sub-region of that tainted
// symbol, it's also tainted.
if (const TaintedSubRegions *Regs =
get<DerivedSymTaint>(SD->getParentSymbol())) {
const TypedValueRegion *R = SD->getRegion();
for (auto I : *Regs) {
// FIXME: The logic to identify tainted regions could be more
// complete. For example, this would not currently identify
// overlapping fields in a union as tainted. To identify this we can
// check for overlapping/nested byte offsets.
if (Kind == I.second &&
(R == I.first || R->isSubRegionOf(I.first)))
return true;
}
}
}
// If memory region is tainted, data is also tainted.
if (const SymbolRegionValue *SRV = dyn_cast<SymbolRegionValue>(*SI))
Tainted = Tainted || isTainted(SRV->getRegion(), Kind);
if (const SymbolRegionValue *SRV = dyn_cast<SymbolRegionValue>(*SI)) {
if (isTainted(SRV->getRegion(), Kind))
return true;
}
// If If this is a SymbolCast from a tainted value, it's also tainted.
if (const SymbolCast *SC = dyn_cast<SymbolCast>(*SI))
Tainted = Tainted || isTainted(SC->getOperand(), Kind);
if (Tainted)
return true;
// If this is a SymbolCast from a tainted value, it's also tainted.
if (const SymbolCast *SC = dyn_cast<SymbolCast>(*SI)) {
if (isTainted(SC->getOperand(), Kind))
return true;
}
}
return Tainted;
return false;
}

View File

@ -496,7 +496,10 @@ class RegionStoreManager : public StoreManager {
Optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override {
RegionBindingsRef B = getRegionBindings(S);
return B.getDefaultBinding(R);
// Default bindings are always applied over a base region so look up the
// base region's default binding, otherwise the lookup will fail when R
// is at an offset from R->getBaseRegion().
return B.getDefaultBinding(R->getBaseRegion());
}
SVal getBinding(RegionBindingsConstRef B, Loc L, QualType T = QualType());

View File

@ -8,18 +8,61 @@
typedef unsigned char uint8_t;
typedef __typeof__(sizeof(int)) size_t;
typedef __typeof__((char*)0-(char*)0) ptrdiff_t;
void *memmove(void *s1, const void *s2, size_t n);
template <typename T, typename Ptr, typename Ref> struct __iterator {
typedef __iterator<T, T *, T &> iterator;
typedef __iterator<T, const T *, const T &> const_iterator;
namespace std {
struct input_iterator_tag { };
struct output_iterator_tag { };
struct forward_iterator_tag : public input_iterator_tag { };
struct bidirectional_iterator_tag : public forward_iterator_tag { };
struct random_access_iterator_tag : public bidirectional_iterator_tag { };
__iterator(const Ptr p) : ptr(p) {}
template <typename Iterator> struct iterator_traits {
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
typedef typename Iterator::iterator_category iterator_category;
};
}
template <typename T, typename Ptr, typename Ref> struct __vector_iterator {
typedef __vector_iterator<T, T *, T &> iterator;
typedef __vector_iterator<T, const T *, const T &> const_iterator;
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef std::random_access_iterator_tag iterator_category;
__vector_iterator(const Ptr p = 0) : ptr(p) {}
__vector_iterator(const iterator &rhs): ptr(rhs.base()) {}
__vector_iterator<T, Ptr, Ref> operator++() { ++ ptr; return *this; }
__vector_iterator<T, Ptr, Ref> operator++(int) {
auto tmp = *this;
++ ptr;
return tmp;
}
__vector_iterator<T, Ptr, Ref> operator--() { -- ptr; return *this; }
__vector_iterator<T, Ptr, Ref> operator--(int) {
auto tmp = *this; -- ptr;
return tmp;
}
__vector_iterator<T, Ptr, Ref> operator+(difference_type n) {
return ptr + n;
}
__vector_iterator<T, Ptr, Ref> operator-(difference_type n) {
return ptr - n;
}
__vector_iterator<T, Ptr, Ref> operator+=(difference_type n) {
return ptr += n;
}
__vector_iterator<T, Ptr, Ref> operator-=(difference_type n) {
return ptr -= n;
}
__iterator<T, Ptr, Ref> operator++() { return *this; }
__iterator<T, Ptr, Ref> operator++(int) { return *this; }
__iterator<T, Ptr, Ref> operator--() { return *this; }
__iterator<T, Ptr, Ref> operator--(int) { return *this; }
Ref operator*() const { return *ptr; }
Ptr operator->() const { return *ptr; }
@ -29,10 +72,136 @@ template <typename T, typename Ptr, typename Ref> struct __iterator {
bool operator!=(const iterator &rhs) const { return ptr != rhs.ptr; }
bool operator!=(const const_iterator &rhs) const { return ptr != rhs.ptr; }
const Ptr& base() const { return ptr; }
private:
Ptr ptr;
};
template <typename T, typename Ptr, typename Ref> struct __deque_iterator {
typedef __deque_iterator<T, T *, T &> iterator;
typedef __deque_iterator<T, const T *, const T &> const_iterator;
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef std::random_access_iterator_tag iterator_category;
__deque_iterator(const Ptr p = 0) : ptr(p) {}
__deque_iterator(const iterator &rhs): ptr(rhs.base()) {}
__deque_iterator<T, Ptr, Ref> operator++() { ++ ptr; return *this; }
__deque_iterator<T, Ptr, Ref> operator++(int) {
auto tmp = *this;
++ ptr;
return tmp;
}
__deque_iterator<T, Ptr, Ref> operator--() { -- ptr; return *this; }
__deque_iterator<T, Ptr, Ref> operator--(int) {
auto tmp = *this; -- ptr;
return tmp;
}
__deque_iterator<T, Ptr, Ref> operator+(difference_type n) {
return ptr + n;
}
__deque_iterator<T, Ptr, Ref> operator-(difference_type n) {
return ptr - n;
}
__deque_iterator<T, Ptr, Ref> operator+=(difference_type n) {
return ptr += n;
}
__deque_iterator<T, Ptr, Ref> operator-=(difference_type n) {
return ptr -= n;
}
Ref operator*() const { return *ptr; }
Ptr operator->() const { return *ptr; }
bool operator==(const iterator &rhs) const { return ptr == rhs.ptr; }
bool operator==(const const_iterator &rhs) const { return ptr == rhs.ptr; }
bool operator!=(const iterator &rhs) const { return ptr != rhs.ptr; }
bool operator!=(const const_iterator &rhs) const { return ptr != rhs.ptr; }
const Ptr& base() const { return ptr; }
private:
Ptr ptr;
};
template <typename T, typename Ptr, typename Ref> struct __list_iterator {
typedef __list_iterator<T, __typeof__(T::data) *, __typeof__(T::data) &> iterator;
typedef __list_iterator<T, const __typeof__(T::data) *, const __typeof__(T::data) &> const_iterator;
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef std::bidirectional_iterator_tag iterator_category;
__list_iterator(T* it = 0) : item(it) {}
__list_iterator(const iterator &rhs): item(rhs.base()) {}
__list_iterator<T, Ptr, Ref> operator++() { item = item->next; return *this; }
__list_iterator<T, Ptr, Ref> operator++(int) {
auto tmp = *this;
item = item->next;
return tmp;
}
__list_iterator<T, Ptr, Ref> operator--() { item = item->prev; return *this; }
__list_iterator<T, Ptr, Ref> operator--(int) {
auto tmp = *this;
item = item->prev;
return tmp;
}
Ref operator*() const { return item->data; }
Ptr operator->() const { return item->data; }
bool operator==(const iterator &rhs) const { return item == rhs->item; }
bool operator==(const const_iterator &rhs) const { return item == rhs->item; }
bool operator!=(const iterator &rhs) const { return item != rhs->item; }
bool operator!=(const const_iterator &rhs) const { return item != rhs->item; }
const T* &base() const { return item; }
private:
T* item;
};
template <typename T, typename Ptr, typename Ref> struct __fwdl_iterator {
typedef __fwdl_iterator<T, __typeof__(T::data) *, __typeof__(T::data) &> iterator;
typedef __fwdl_iterator<T, const __typeof__(T::data) *, const __typeof__(T::data) &> const_iterator;
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef std::forward_iterator_tag iterator_category;
__fwdl_iterator(T* it = 0) : item(it) {}
__fwdl_iterator(const iterator &rhs): item(rhs.base()) {}
__fwdl_iterator<T, Ptr, Ref> operator++() { item = item->next; return *this; }
__fwdl_iterator<T, Ptr, Ref> operator++(int) {
auto tmp = *this;
item = item->next;
return tmp;
}
Ref operator*() const { return item->data; }
Ptr operator->() const { return item->data; }
bool operator==(const iterator &rhs) const { return item == rhs->item; }
bool operator==(const const_iterator &rhs) const { return item == rhs->item; }
bool operator!=(const iterator &rhs) const { return item != rhs->item; }
bool operator!=(const const_iterator &rhs) const { return item != rhs->item; }
const T* &base() const { return item; }
private:
T* item;
};
namespace std {
template <class T1, class T2>
struct pair {
@ -43,29 +212,45 @@ namespace std {
pair(const T1 &a, const T2 &b) : first(a), second(b) {}
template<class U1, class U2>
pair(const pair<U1, U2> &other) : first(other.first), second(other.second) {}
pair(const pair<U1, U2> &other) : first(other.first),
second(other.second) {}
};
typedef __typeof__(sizeof(int)) size_t;
template <class T> class initializer_list;
template< class T > struct remove_reference {typedef T type;};
template< class T > struct remove_reference<T&> {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};
template<class T>
typename remove_reference<T>::type&& move(T&& a) {
typedef typename remove_reference<T>::type&& RvalRef;
return static_cast<RvalRef>(a);
}
template<typename T>
class vector {
typedef __iterator<T, T *, T &> iterator;
typedef __iterator<T, const T *, const T &> const_iterator;
typedef T value_type;
typedef size_t size_type;
typedef __vector_iterator<T, T *, T &> iterator;
typedef __vector_iterator<T, const T *, const T &> const_iterator;
T *_start;
T *_finish;
T *_end_of_storage;
public:
vector() : _start(0), _finish(0), _end_of_storage(0) {}
template <typename InputIterator>
vector(InputIterator first, InputIterator last);
vector(const vector &other);
vector(vector &&other);
~vector();
size_t size() const {
return size_t(_finish - _start);
}
void push_back();
T pop_back();
T &operator[](size_t n) {
return _start[n];
@ -77,10 +262,124 @@ namespace std {
iterator begin() { return iterator(_start); }
const_iterator begin() const { return const_iterator(_start); }
const_iterator cbegin() const { return const_iterator(_start); }
iterator end() { return iterator(_finish); }
const_iterator end() const { return const_iterator(_finish); }
const_iterator cend() const { return const_iterator(_finish); }
T& front() { return *begin(); }
const T& front() const { return *begin(); }
T& back() { return *(end() - 1); }
const T& back() const { return *(end() - 1); }
};
template<typename T>
class list {
struct __item {
T data;
__item *prev, *next;
} *_start, *_finish;
public:
typedef T value_type;
typedef size_t size_type;
typedef __list_iterator<__item, T *, T &> iterator;
typedef __list_iterator<__item, const T *, const T &> const_iterator;
list() : _start(0), _finish(0) {}
template <typename InputIterator>
list(InputIterator first, InputIterator last);
list(const list &other);
list(list &&other);
~list();
list& operator=(const list &other);
list& operator=(list &&other);
list& operator=(std::initializer_list<T> ilist);
iterator begin() { return iterator(_start); }
const_iterator begin() const { return const_iterator(_start); }
const_iterator cbegin() const { return const_iterator(_start); }
iterator end() { return iterator(_finish); }
const_iterator end() const { return const_iterator(_finish); }
const_iterator cend() const { return const_iterator(_finish); }
T& front() { return *begin(); }
const T& front() const { return *begin(); }
T& back() { return *--end(); }
const T& back() const { return *--end(); }
};
template<typename T>
class deque {
typedef T value_type;
typedef size_t size_type;
typedef __deque_iterator<T, T *, T &> iterator;
typedef __deque_iterator<T, const T *, const T &> const_iterator;
T *_start;
T *_finish;
T *_end_of_storage;
public:
deque() : _start(0), _finish(0), _end_of_storage(0) {}
template <typename InputIterator>
deque(InputIterator first, InputIterator last);
deque(const deque &other);
deque(deque &&other);
~deque();
size_t size() const {
return size_t(_finish - _start);
}
T &operator[](size_t n) {
return _start[n];
}
const T &operator[](size_t n) const {
return _start[n];
}
iterator begin() { return iterator(_start); }
const_iterator begin() const { return const_iterator(_start); }
const_iterator cbegin() const { return const_iterator(_start); }
iterator end() { return iterator(_finish); }
const_iterator end() const { return const_iterator(_finish); }
const_iterator cend() const { return const_iterator(_finish); }
T& front() { return *begin(); }
const T& front() const { return *begin(); }
T& back() { return *(end() - 1); }
const T& back() const { return *(end() - 1); }
};
template<typename T>
class forward_list {
struct __item {
T data;
__item *next;
} *_start;
public:
typedef T value_type;
typedef size_t size_type;
typedef __fwdl_iterator<__item, T *, T &> iterator;
typedef __fwdl_iterator<__item, const T *, const T &> const_iterator;
forward_list() : _start(0) {}
template <typename InputIterator>
forward_list(InputIterator first, InputIterator last);
forward_list(const forward_list &other);
forward_list(forward_list &&other);
~forward_list();
iterator begin() { return iterator(_start); }
const_iterator begin() const { return const_iterator(_start); }
const_iterator cbegin() const { return const_iterator(_start); }
iterator end() { return iterator(); }
const_iterator end() const { return const_iterator(); }
const_iterator cend() const { return const_iterator(); }
T& front() { return *begin(); }
const T& front() const { return *begin(); }
};
class exception {
public:
exception() throw();
@ -247,41 +546,41 @@ namespace std {
OutputIter copy_backward(InputIter II, InputIter IE, OutputIter OI) {
return __copy_backward(II, IE, OI);
}
}
template <class BidirectionalIterator, class Distance>
void __advance (BidirectionalIterator& it, Distance n,
std::bidirectional_iterator_tag) {
if (n >= 0) while(n-- > 0) ++it; else while (n++<0) --it;
}
template <class RandomAccessIterator, class Distance>
void __advance (RandomAccessIterator& it, Distance n,
std::random_access_iterator_tag) {
it += n;
}
namespace std {
template <class InputIterator, class Distance>
void advance (InputIterator& it, Distance n) {
__advance(it, n, typename InputIterator::iterator_category());
}
template <class BidirectionalIterator>
BidirectionalIterator
prev (BidirectionalIterator it,
typename iterator_traits<BidirectionalIterator>::difference_type n =
1) {
advance(it, -n);
return it;
}
template <class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T &val);
template <class ForwardIterator1, class ForwardIterator2>
ForwardIterator1 find_end(ForwardIterator1 first1, ForwardIterator1 last1,
ForwardIterator2 first2, ForwardIterator2 last2);
template <class ForwardIterator1, class ForwardIterator2>
ForwardIterator1 find_first_of(ForwardIterator1 first1,
ForwardIterator1 last1,
ForwardIterator2 first2,
ForwardIterator2 last2);
template <class InputIterator, class UnaryPredicate>
InputIterator find_if(InputIterator first, InputIterator last,
UnaryPredicate pred);
template <class InputIterator, class UnaryPredicate>
InputIterator find_if_not(InputIterator first, InputIterator last,
UnaryPredicate pred);
template <class InputIterator, class T>
InputIterator lower_bound(InputIterator first, InputIterator last,
const T &val);
template <class InputIterator, class T>
InputIterator upper_bound(InputIterator first, InputIterator last,
const T &val);
template <class ForwardIterator1, class ForwardIterator2>
ForwardIterator1 search(ForwardIterator1 first1, ForwardIterator1 last1,
ForwardIterator2 first2, ForwardIterator2 last2);
template <class ForwardIterator1, class ForwardIterator2>
ForwardIterator1 search_n(ForwardIterator1 first1, ForwardIterator1 last1,
ForwardIterator2 first2, ForwardIterator2 last2);
struct input_iterator_tag { };
struct output_iterator_tag { };
struct forward_iterator_tag : public input_iterator_tag { };
struct bidirectional_iterator_tag : public forward_iterator_tag { };
struct random_access_iterator_tag : public bidirectional_iterator_tag { };
template <class InputIterator, class OutputIterator>
OutputIterator copy(InputIterator first, InputIterator last,
OutputIterator result);
}

View File

@ -19,6 +19,6 @@ class C {
void testCopyNull(C *I, C *E) {
std::copy(I, E, (C *)0);
#ifndef SUPPRESSED
// expected-warning@../Inputs/system-header-simulator-cxx.h:191 {{Called C++ object pointer is null}}
// expected-warning@../Inputs/system-header-simulator-cxx.h:490 {{Called C++ object pointer is null}}
#endif
}

View File

@ -1,205 +0,0 @@
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorPastEnd -analyzer-eagerly-assume -analyzer-config c++-container-inlining=false %s -verify
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorPastEnd -analyzer-eagerly-assume -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify
#include "Inputs/system-header-simulator-cxx.h"
void simple_good(const std::vector<int> &v) {
auto i = v.end();
if (i != v.end())
*i; // no-warning
}
void simple_good_negated(const std::vector<int> &v) {
auto i = v.end();
if (!(i == v.end()))
*i; // no-warning
}
void simple_bad(const std::vector<int> &v) {
auto i = v.end();
*i; // expected-warning{{Iterator accessed past its end}}
}
void copy(const std::vector<int> &v) {
auto i1 = v.end();
auto i2 = i1;
*i2; // expected-warning{{Iterator accessed past its end}}
}
void decrease(const std::vector<int> &v) {
auto i = v.end();
--i;
*i; // no-warning
}
void copy_and_decrease1(const std::vector<int> &v) {
auto i1 = v.end();
auto i2 = i1;
--i1;
*i1; // no-warning
}
void copy_and_decrease2(const std::vector<int> &v) {
auto i1 = v.end();
auto i2 = i1;
--i1;
*i2; // expected-warning{{Iterator accessed past its end}}
}
void copy_and_increase1(const std::vector<int> &v) {
auto i1 = v.begin();
auto i2 = i1;
++i1;
if (i1 == v.end())
*i2; // no-warning
}
void copy_and_increase2(const std::vector<int> &v) {
auto i1 = v.begin();
auto i2 = i1;
++i1;
if (i2 == v.end())
*i2; // expected-warning{{Iterator accessed past its end}}
}
void good_find(std::vector<int> &vec, int e) {
auto first = std::find(vec.begin(), vec.end(), e);
if (vec.end() != first)
*first; // no-warning
}
void bad_find(std::vector<int> &vec, int e) {
auto first = std::find(vec.begin(), vec.end(), e);
*first; // expected-warning{{Iterator accessed past its end}}
}
void good_find_end(std::vector<int> &vec, std::vector<int> &seq) {
auto last = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end());
if (vec.end() != last)
*last; // no-warning
}
void bad_find_end(std::vector<int> &vec, std::vector<int> &seq) {
auto last = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end());
*last; // expected-warning{{Iterator accessed past its end}}
}
void good_find_first_of(std::vector<int> &vec, std::vector<int> &seq) {
auto first =
std::find_first_of(vec.begin(), vec.end(), seq.begin(), seq.end());
if (vec.end() != first)
*first; // no-warning
}
void bad_find_first_of(std::vector<int> &vec, std::vector<int> &seq) {
auto first = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end());
*first; // expected-warning{{Iterator accessed past its end}}
}
bool odd(int i) { return i % 2; }
void good_find_if(std::vector<int> &vec) {
auto first = std::find_if(vec.begin(), vec.end(), odd);
if (vec.end() != first)
*first; // no-warning
}
void bad_find_if(std::vector<int> &vec, int e) {
auto first = std::find_if(vec.begin(), vec.end(), odd);
*first; // expected-warning{{Iterator accessed past its end}}
}
void good_find_if_not(std::vector<int> &vec) {
auto first = std::find_if_not(vec.begin(), vec.end(), odd);
if (vec.end() != first)
*first; // no-warning
}
void bad_find_if_not(std::vector<int> &vec, int e) {
auto first = std::find_if_not(vec.begin(), vec.end(), odd);
*first; // expected-warning{{Iterator accessed past its end}}
}
void good_lower_bound(std::vector<int> &vec, int e) {
auto first = std::lower_bound(vec.begin(), vec.end(), e);
if (vec.end() != first)
*first; // no-warning
}
void bad_lower_bound(std::vector<int> &vec, int e) {
auto first = std::lower_bound(vec.begin(), vec.end(), e);
*first; // expected-warning{{Iterator accessed past its end}}
}
void good_upper_bound(std::vector<int> &vec, int e) {
auto last = std::lower_bound(vec.begin(), vec.end(), e);
if (vec.end() != last)
*last; // no-warning
}
void bad_upper_bound(std::vector<int> &vec, int e) {
auto last = std::lower_bound(vec.begin(), vec.end(), e);
*last; // expected-warning{{Iterator accessed past its end}}
}
void good_search(std::vector<int> &vec, std::vector<int> &seq) {
auto first = std::search(vec.begin(), vec.end(), seq.begin(), seq.end());
if (vec.end() != first)
*first; // no-warning
}
void bad_search(std::vector<int> &vec, std::vector<int> &seq) {
auto first = std::search(vec.begin(), vec.end(), seq.begin(), seq.end());
*first; // expected-warning{{Iterator accessed past its end}}
}
void good_search_n(std::vector<int> &vec, std::vector<int> &seq) {
auto nth = std::search_n(vec.begin(), vec.end(), seq.begin(), seq.end());
if (vec.end() != nth)
*nth; // no-warning
}
void bad_search_n(std::vector<int> &vec, std::vector<int> &seq) {
auto nth = std::search_n(vec.begin(), vec.end(), seq.begin(), seq.end());
*nth; // expected-warning{{Iterator accessed past its end}}
}
template <class InputIterator, class T>
InputIterator nonStdFind(InputIterator first, InputIterator last,
const T &val) {
for (auto i = first; i != last; ++i) {
if (*i == val) {
return i;
}
}
return last;
}
void good_non_std_find(std::vector<int> &vec, int e) {
auto first = nonStdFind(vec.begin(), vec.end(), e);
if (vec.end() != first)
*first; // no-warning
}
void bad_non_std_find(std::vector<int> &vec, int e) {
auto first = nonStdFind(vec.begin(), vec.end(), e);
*first; // expected-warning{{Iterator accessed past its end}}
}
void tricky(std::vector<int> &vec, int e) {
const auto first = vec.begin();
const auto comp1 = (first != vec.end()), comp2 = (first == vec.end());
if (comp1)
*first;
}
void loop(std::vector<int> &vec, int e) {
auto start = vec.begin();
while (true) {
auto item = std::find(start, vec.end(), e);
if (item == vec.end())
break;
*item; // no-warning
start = ++item; // no-warning
}
}

View File

@ -0,0 +1,19 @@
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-eagerly-assume -analyzer-config c++-container-inlining=false %s -verify
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-eagerly-assume -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify
#include "Inputs/system-header-simulator-cxx.h"
void clang_analyzer_warnIfReached();
void simple_good_end(const std::vector<int> &v) {
auto i = v.end();
if (i != v.end()) {
clang_analyzer_warnIfReached();
*i; // no-warning
}
}
void simple_bad_end(const std::vector<int> &v) {
auto i = v.end();
*i; // expected-warning{{Iterator accessed outside of its range}}
}

View File

@ -176,6 +176,42 @@ ok22(void) {
pthread_mutex_unlock(pmtx); // no-warning
}
void ok23(void) {
if (pthread_mutex_destroy(&mtx1) != 0) // no-warning
pthread_mutex_destroy(&mtx1); // no-warning
}
void ok24(void) {
if (pthread_mutex_destroy(&mtx1) != 0) // no-warning
pthread_mutex_lock(&mtx1); // no-warning
}
void ok25(void) {
if (pthread_mutex_destroy(&mtx1) != 0) // no-warning
pthread_mutex_unlock(&mtx1); // no-warning
}
void ok26(void) {
pthread_mutex_unlock(&mtx1); // no-warning
if (pthread_mutex_destroy(&mtx1) != 0) // no-warning
pthread_mutex_lock(&mtx1); // no-warning
}
void ok27(void) {
pthread_mutex_unlock(&mtx1); // no-warning
if (pthread_mutex_destroy(&mtx1) != 0) // no-warning
pthread_mutex_lock(&mtx1); // no-warning
else
pthread_mutex_init(&mtx1, NULL); // no-warning
}
void ok28(void) {
if (pthread_mutex_destroy(&mtx1) != 0) { // no-warning
pthread_mutex_lock(&mtx1); // no-warning
pthread_mutex_unlock(&mtx1); // no-warning
pthread_mutex_destroy(&mtx1); // no-warning
}
}
void
bad1(void)
@ -392,3 +428,46 @@ bad26(void)
pthread_mutex_unlock(&mtx1); // no-warning
pthread_mutex_init(&mtx1, NULL); // expected-warning{{This lock has already been initialized}}
}
void bad27(void) {
pthread_mutex_unlock(&mtx1); // no-warning
int ret = pthread_mutex_destroy(&mtx1); // no-warning
if (ret != 0) // no-warning
pthread_mutex_lock(&mtx1); // no-warning
else
pthread_mutex_unlock(&mtx1); // expected-warning{{This lock has already been destroyed}}
}
void bad28(void) {
pthread_mutex_unlock(&mtx1); // no-warning
int ret = pthread_mutex_destroy(&mtx1); // no-warning
if (ret != 0) // no-warning
pthread_mutex_lock(&mtx1); // no-warning
else
pthread_mutex_lock(&mtx1); // expected-warning{{This lock has already been destroyed}}
}
void bad29(void) {
pthread_mutex_lock(&mtx1); // no-warning
pthread_mutex_unlock(&mtx1); // no-warning
if (pthread_mutex_destroy(&mtx1) != 0) // no-warning
pthread_mutex_init(&mtx1, NULL); // expected-warning{{This lock has already been initialized}}
else
pthread_mutex_init(&mtx1, NULL); // no-warning
}
void bad30(void) {
pthread_mutex_lock(&mtx1); // no-warning
pthread_mutex_unlock(&mtx1); // no-warning
if (pthread_mutex_destroy(&mtx1) != 0) // no-warning
pthread_mutex_init(&mtx1, NULL); // expected-warning{{This lock has already been initialized}}
else
pthread_mutex_destroy(&mtx1); // expected-warning{{This lock has already been destroyed}}
}
void bad31(void) {
int ret = pthread_mutex_destroy(&mtx1); // no-warning
pthread_mutex_lock(&mtx1); // expected-warning{{This lock has already been destroyed}}
if (ret != 0)
pthread_mutex_lock(&mtx1);
}

View File

@ -192,20 +192,41 @@ void testStruct() {
void testStructArray() {
struct {
char buf[16];
struct {
int length;
} st[1];
} tainted;
int length;
} tainted[4];
char buffer[16];
char dstbuf[16], srcbuf[16];
int sock;
sock = socket(AF_INET, SOCK_STREAM, 0);
read(sock, &tainted.buf[0], sizeof(tainted.buf));
read(sock, &tainted.st[0], sizeof(tainted.st));
// FIXME: tainted.st[0].length should be marked tainted
__builtin_memcpy(buffer, tainted.buf, tainted.st[0].length); // no-warning
__builtin_memset(srcbuf, 0, sizeof(srcbuf));
read(sock, &tainted[0], sizeof(tainted));
__builtin_memcpy(dstbuf, srcbuf, tainted[0].length); // expected-warning {{Untrusted data is used to specify the buffer size}}
__builtin_memset(&tainted, 0, sizeof(tainted));
read(sock, &tainted, sizeof(tainted));
__builtin_memcpy(dstbuf, srcbuf, tainted[0].length); // expected-warning {{Untrusted data is used to specify the buffer size}}
__builtin_memset(&tainted, 0, sizeof(tainted));
// If we taint element 1, we should not raise an alert on taint for element 0 or element 2
read(sock, &tainted[1], sizeof(tainted));
__builtin_memcpy(dstbuf, srcbuf, tainted[0].length); // no-warning
__builtin_memcpy(dstbuf, srcbuf, tainted[2].length); // no-warning
}
void testUnion() {
union {
int x;
char y[4];
} tainted;
char buffer[4];
int sock = socket(AF_INET, SOCK_STREAM, 0);
read(sock, &tainted.y, sizeof(tainted.y));
// FIXME: overlapping regions aren't detected by isTainted yet
__builtin_memcpy(buffer, tainted.y, tainted.x);
}
int testDivByZero() {

82
test/CodeGen/altivec-ct.c Normal file
View File

@ -0,0 +1,82 @@
// RUN: %clang_cc1 -triple powerpc64le-linux-gnu -S -O0 -o - %s -target-feature +altivec -target-feature +vsx | FileCheck %s -check-prefix=CHECK -check-prefix=VSX
// RUN: %clang_cc1 -triple powerpc-linux-gnu -S -O0 -o - %s -target-feature +altivec -target-feature -vsx | FileCheck %s
// REQUIRES: powerpc-registered-target
#include <altivec.h>
// CHECK-LABEL: test1
// CHECK: vcfsx
vector float test1(vector int x) {
return vec_ctf(x, 0);
}
// CHECK-LABEL: test2
// CHECK: vcfux
vector float test2(vector unsigned int x) {
return vec_ctf(x, 0);
}
#ifdef __VSX__
// VSX-LABEL: test3
vector double test3(vector signed long long x) {
return vec_ctf(x, 0);
}
// VSX-LABEL: test4
vector double test4(vector unsigned long long x) {
return vec_ctf(x, 0);
}
#endif
// CHECK-LABEL: test5
// CHECK: vcfsx
vector float test5(vector int x) {
return vec_vcfsx(x, 0);
}
// CHECK-LABEL: test6
// CHECK: vcfux
vector float test6(vector unsigned int x) {
return vec_vcfux(x, 0);
}
// CHECK-LABEL: test7
// CHECK: vctsxs
vector int test7(vector float x) {
return vec_cts(x, 0);
}
#ifdef __VSX__
// VSX-LABEL: test8
vector signed long long test8(vector double x) {
return vec_cts(x, 0);
}
#endif
// CHECK-LABEL: test9
// CHECK: vctsxs
vector int test9(vector float x) {
return vec_vctsxs(x, 0);
}
// CHECK-LABEL: test10
// CHECK: vctuxs
vector unsigned test10(vector float x) {
return vec_ctu(x, 0);
}
#ifdef __VSX__
// VSX-LABEL: test11
vector unsigned long long test11(vector double x) {
return vec_ctu(x, 0);
}
#endif
// CHECK-LABEL: test12
// CHECK: vctuxs
vector unsigned test12(vector float x) {
return vec_vctuxs(x, 0);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
// RUN: %clang_cc1 -emit-llvm %s -o - | grep load | grep "4 x float" | not grep "align 4"
// RUN: %clang_cc1 -emit-llvm %s -o - | grep load | grep "4 x float" | grep "align 16"
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | grep load | grep "4 x float" | not grep "align 4"
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | grep load | grep "4 x float" | grep "align 16"
// PR3432
// rdar://6536377

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 %s -emit-llvm -o - -O0 | FileCheck %s
// RUN: %clang_cc1 %s -triple x86_64-unknown-linux-gnu -emit-llvm -o - -O0 | FileCheck %s
typedef unsigned char uchar4 __attribute((ext_vector_type(4)));
typedef unsigned int int4 __attribute((ext_vector_type(4)));

View File

@ -9,15 +9,12 @@
#pragma unknown
#endif
#ifdef WIBBLE
#include "honk"
#else
int foo();
#include "foo"
int bar;
#endif
// This test checks for detection of the preamble of a file, which
// includes all of the starting comments and #includes. Note that any
// changes to the preamble part of this file must be mirrored in
// Inputs/preamble.txt, since we diff against it.
// includes all of the starting comments and #includes.
// RUN: %clang_cc1 -print-preamble %s > %t
// RUN: echo END. >> %t
@ -33,4 +30,6 @@ int foo();
// CHECK-NEXT: #endif
// CHECK-NEXT: #pragma unknown
// CHECK-NEXT: #endif
// CHECK-NEXT: #ifdef WIBBLE
// CHECK-NEXT: #include "foo"
// CHECK-NEXT: END.

19
test/Lexer/preamble2.c Normal file
View File

@ -0,0 +1,19 @@
// Preamble detection test: header with an include guard.
#ifndef HEADER_H
#define HEADER_H
#include "foo"
int bar;
#endif
// This test checks for detection of the preamble of a file, which
// includes all of the starting comments and #includes.
// RUN: %clang_cc1 -print-preamble %s > %t
// RUN: echo END. >> %t
// RUN: FileCheck < %t %s
// CHECK: // Preamble detection test: header with an include guard.
// CHECK-NEXT: #ifndef HEADER_H
// CHECK-NEXT: #define HEADER_H
// CHECK-NEXT: #include "foo"
// CHECK-NEXT: END.

View File

@ -0,0 +1,2 @@
#include "c.h"
T a();

View File

@ -0,0 +1,2 @@
#include "c.h"
T b();

View File

@ -0,0 +1,4 @@
#ifndef C_H
#define C_H
using T = int;
#endif

View File

@ -1,2 +1,7 @@
module fwd { header "fwd.h" export * }
module file { header "file.h" header "file2.h" export * }
module nested {
module a { header "a.h" }
module b { header "b.h" }
module c { header "c.h" }
}

View File

@ -0,0 +1,59 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -fmodule-name=nested -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -E -o %t/no-rewrite.ii
// RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -fmodule-name=nested -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -E -frewrite-includes -o %t/rewrite.ii
// RUN: FileCheck %s --input-file %t/no-rewrite.ii --check-prefix=CHECK --check-prefix=NO-REWRITE
// RUN: FileCheck %s --input-file %t/rewrite.ii --check-prefix=CHECK --check-prefix=REWRITE
// Check that we can build a module from the preprocessed output.
// FIXME: For now, the files need to exist.
// RUN: touch %t/a.h %t/b.h %t/c.h
// RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -fmodule-name=nested -x c++-module-map-cpp-output %t/no-rewrite.ii -emit-module -o %t/no-rewrite.pcm
// RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -fmodule-name=nested -x c++-module-map-cpp-output %t/rewrite.ii -emit-module -o %t/rewrite.pcm
// Check the module we built works.
// RUN: %clang_cc1 -fmodules -fmodule-file=%t/no-rewrite.pcm %s -I%t -verify -fno-modules-error-recovery
// RUN: %clang_cc1 -fmodules -fmodule-file=%t/rewrite.pcm %s -I%t -verify -fno-modules-error-recovery -DREWRITE
// == module map
// CHECK: # 1 "{{.*}}module.modulemap"
// CHECK: module nested {
// CHECK: module a {
// CHECK: header "a.h"
// CHECK: }
// CHECK: module b {
// CHECK: header "b.h"
// CHECK: }
// CHECK: module c {
// CHECK: header "c.h"
// CHECK: }
// CHECK: }
// CHECK: #pragma clang module begin nested.a
// CHECK: #pragma clang module begin nested.c
// CHECK: using T = int;
// CHECK: #pragma clang module end
// CHECK: T a();
// CHECK: #pragma clang module end
// CHECK: #pragma clang module begin nested.b
// CHECK: #pragma clang module import nested.c
// CHECK-NOT: #pragma clang module begin nested.c
// CHECK-NOT: using T = int;
// CHECK-NOT: #pragma clang module end
// CHECK: T b();
// CHECK: #pragma clang module end
// CHECK: #pragma clang module import nested.c
#pragma clang module import nested.b
int n = b();
T c; // expected-error {{must be imported}}
#ifdef REWRITE
// expected-note@rewrite.ii:* {{declar}}
#else
// expected-note@no-rewrite.ii:* {{declar}}
#endif

View File

@ -0,0 +1,12 @@
// RUN: %clang_cc1 -x c++-module-map %s -fmodule-name=a -verify
module a {
module b {
requires cplusplus11
}
}
#pragma clang module contents
// expected-error@3 {{module 'a.b' requires feature 'cplusplus11'}}
#pragma clang module begin a.b // expected-note {{entering module 'a' due to this pragma}}
int f();
int g() { f(); }
#pragma clang module end // expected-error {{no matching '#pragma clang module begin'}}

View File

@ -4,7 +4,7 @@ void foo() { }
int main(int argc, char **argv) {
int a;
#pragma omp target data // expected-error {{expected at least one map clause for '#pragma omp target data'}}
#pragma omp target data // expected-error {{expected at least one 'map' or 'use_device_ptr' clause for '#pragma omp target data'}}
{}
L1:
foo();

View File

@ -4,7 +4,7 @@
int main(int argc, char **argv) {
int r;
#pragma omp target enter data // expected-error {{expected at least one map clause for '#pragma omp target enter data'}}
#pragma omp target enter data // expected-error {{expected at least one 'map' clause for '#pragma omp target enter data'}}
#pragma omp target enter data map(r) // expected-error {{map type must be specified for '#pragma omp target enter data'}}
#pragma omp target enter data map(tofrom: r) // expected-error {{map type 'tofrom' is not allowed for '#pragma omp target enter data'}}

View File

@ -6,7 +6,7 @@ int main(int argc, char **argv) {
#pragma omp nowait target enter data map(to: i) // expected-error {{expected an OpenMP directive}}
#pragma omp target nowait enter data map(to: i) // expected-warning {{extra tokens at the end of '#pragma omp target' are ignored}}
#pragma omp target enter nowait data map(to: i) // expected-error {{expected an OpenMP directive}}
#pragma omp target enter data nowait() map(to: i) // expected-warning {{extra tokens at the end of '#pragma omp target enter data' are ignored}} expected-error {{expected at least one map clause for '#pragma omp target enter data'}}
#pragma omp target enter data nowait() map(to: i) // expected-warning {{extra tokens at the end of '#pragma omp target enter data' are ignored}} expected-error {{expected at least one 'map' clause for '#pragma omp target enter data'}}
#pragma omp target enter data map(to: i) nowait( // expected-warning {{extra tokens at the end of '#pragma omp target enter data' are ignored}}
#pragma omp target enter data map(to: i) nowait (argc)) // expected-warning {{extra tokens at the end of '#pragma omp target enter data' are ignored}}
#pragma omp target enter data map(to: i) nowait device (-10u)

View File

@ -4,7 +4,7 @@
int main(int argc, char **argv) {
int r;
#pragma omp target exit data // expected-error {{expected at least one map clause for '#pragma omp target exit data'}}
#pragma omp target exit data // expected-error {{expected at least one 'map' clause for '#pragma omp target exit data'}}
#pragma omp target exit data map(r) // expected-error {{map type must be specified for '#pragma omp target exit data'}}
#pragma omp target exit data map(tofrom: r) // expected-error {{map type 'tofrom' is not allowed for '#pragma omp target exit data'}}

View File

@ -6,7 +6,7 @@ int main(int argc, char **argv) {
#pragma omp nowait target exit data map(from: i) // expected-error {{expected an OpenMP directive}}
#pragma omp target nowait exit data map(from: i) // expected-warning {{extra tokens at the end of '#pragma omp target' are ignored}}
#pragma omp target exit nowait data map(from: i) // expected-error {{expected an OpenMP directive}}
#pragma omp target exit data nowait() map(from: i) // expected-warning {{extra tokens at the end of '#pragma omp target exit data' are ignored}} expected-error {{expected at least one map clause for '#pragma omp target exit data'}}
#pragma omp target exit data nowait() map(from: i) // expected-warning {{extra tokens at the end of '#pragma omp target exit data' are ignored}} expected-error {{expected at least one 'map' clause for '#pragma omp target exit data'}}
#pragma omp target exit data map(from: i) nowait( // expected-warning {{extra tokens at the end of '#pragma omp target exit data' are ignored}}
#pragma omp target exit data map(from: i) nowait (argc)) // expected-warning {{extra tokens at the end of '#pragma omp target exit data' are ignored}}
#pragma omp target exit data map(from: i) nowait device (-10u)

View File

@ -467,7 +467,7 @@ int main(int argc, char **argv) {
int y;
int to, tofrom, always;
const int (&l)[5] = da;
#pragma omp target data map // expected-error {{expected '(' after 'map'}} expected-error {{expected at least one map clause for '#pragma omp target data'}}
#pragma omp target data map // expected-error {{expected '(' after 'map'}} expected-error {{expected at least one 'map' or 'use_device_ptr' clause for '#pragma omp target data'}}
#pragma omp target data map( // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-error {{expected expression}}
#pragma omp target data map() // expected-error {{expected expression}}
#pragma omp target data map(alloc) // expected-error {{use of undeclared identifier 'alloc'}}

View File

@ -464,7 +464,7 @@ int main(int argc, char **argv) {
int y;
int to, tofrom, always;
const int (&l)[5] = da;
#pragma omp target data map // expected-error {{expected '(' after 'map'}} expected-error {{expected at least one map clause for '#pragma omp target data'}}
#pragma omp target data map // expected-error {{expected '(' after 'map'}} expected-error {{expected at least one 'map' or 'use_device_ptr' clause for '#pragma omp target data'}}
#pragma omp target data map( // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-error {{expected expression}}
#pragma omp target data map() // expected-error {{expected expression}}
#pragma omp target data map(alloc) // expected-error {{use of undeclared identifier 'alloc'}}

View File

@ -0,0 +1,23 @@
// RUN: %clang_cc1 %s -triple spir-unknown-unknown -verify -pedantic -fsyntax-only -cl-std=CL1.2
typedef float float2 __attribute__((ext_vector_type(2)));
typedef long long2 __attribute__((ext_vector_type(2)));
typedef int int2 __attribute__((ext_vector_type(2)));
kernel void foo1(float2 in, global float2 *out) { *out = in + 0.5;} // expected-error {{scalar operand type has greater rank than the type of the vector element. ('float2' (vector of 2 'float' values) and 'double')}}
kernel void foo2(float2 in, global float2 *out) { *out = 0.5 + in;} // expected-error {{scalar operand type has greater rank than the type of the vector element. ('double' and 'float2' (vector of 2 'float' values))}}
kernel void foo3(float2 in, global float2 *out) { *out = 0.5f + in;}
kernel void foo4(long2 in, global long2 *out) { *out = 5 + in;}
kernel void foo5(float2 in, global float2 *out) {
float* f;
*out = f + in; // expected-error{{cannot convert between vector and non-scalar values ('float *' and 'float2' (vector of 2 'float' values))}}
}
kernel void foo6(int2 in, global int2 *out) {
int* f;
*out = f + in; // expected-error{{cannot convert between vector and non-scalar values ('int *' and 'int2' (vector of 2 'int' values))}}
}

View File

@ -4,13 +4,13 @@
kernel void dse_builtins() {
int tmp;
enqueue_kernel(tmp, tmp, tmp, ^(void) { // expected-warning{{implicit declaration of function 'enqueue_kernel' is invalid in C99}}
enqueue_kernel(tmp, tmp, tmp, ^(void) { // expected-error{{implicit declaration of function 'enqueue_kernel' is invalid in OpenCL}}
return;
});
unsigned size = get_kernel_work_group_size(^(void) { // expected-warning{{implicit declaration of function 'get_kernel_work_group_size' is invalid in C99}}
unsigned size = get_kernel_work_group_size(^(void) { // expected-error{{implicit declaration of function 'get_kernel_work_group_size' is invalid in OpenCL}}
return;
});
size = get_kernel_preferred_work_group_size_multiple(^(void) { // expected-warning{{implicit declaration of function 'get_kernel_preferred_work_group_size_multiple' is invalid in C99}}
size = get_kernel_preferred_work_group_size_multiple(^(void) { // expected-error{{implicit declaration of function 'get_kernel_preferred_work_group_size_multiple' is invalid in OpenCL}}
return;
});
}
@ -18,27 +18,48 @@ kernel void dse_builtins() {
void pipe_builtins() {
int tmp;
read_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'read_pipe' is invalid in C99}}
write_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'write_pipe' is invalid in C99}}
foo(void); // expected-error{{implicit declaration of function 'foo' is invalid in OpenCL}}
// expected-note@-1{{'foo' declared here}}
// expected-error@-2{{expected expression}}
boo(); // expected-error{{implicit declaration of function 'boo' is invalid in OpenCL}}
// expected-note@-1{{did you mean 'foo'?}}
reserve_read_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'reserve_read_pipe' is invalid in C99}}
reserve_write_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'reserve_write_pipe' is invalid in C99}}
read_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'read_pipe' is invalid in OpenCL}}
write_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'write_pipe' is invalid in OpenCL}}
work_group_reserve_read_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'work_group_reserve_read_pipe' is invalid in C99}}
work_group_reserve_write_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'work_group_reserve_write_pipe' is invalid in C99}}
reserve_read_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'reserve_read_pipe' is invalid in OpenCL}}
// expected-note@-1{{'reserve_read_pipe' declared here}}
reserve_write_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'reserve_write_pipe' is invalid in OpenCL}}
// expected-note@-1{{did you mean 'reserve_read_pipe'?}}
sub_group_reserve_write_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'sub_group_reserve_write_pipe' is invalid in C99}}
sub_group_reserve_read_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'sub_group_reserve_read_pipe' is invalid in C99}}
work_group_reserve_read_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'work_group_reserve_read_pipe' is invalid in OpenCL}}
// expected-note@-1 2{{'work_group_reserve_read_pipe' declared here}}
work_group_reserve_write_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'work_group_reserve_write_pipe' is invalid in OpenCL}}
// expected-note@-1{{did you mean 'work_group_reserve_read_pipe'?}}
// expected-note@-2{{'work_group_reserve_write_pipe' declared here}}
commit_read_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'commit_read_pipe' is invalid in C99}}
commit_write_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'commit_write_pipe' is invalid in C99}}
sub_group_reserve_write_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'sub_group_reserve_write_pipe' is invalid in OpenCL}}
// expected-note@-1{{did you mean 'work_group_reserve_write_pipe'?}}
sub_group_reserve_read_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'sub_group_reserve_read_pipe' is invalid in OpenCL}}
work_group_commit_read_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'work_group_commit_read_pipe' is invalid in C99}}
work_group_commit_write_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'work_group_commit_write_pipe' is invalid in C99}}
commit_read_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'commit_read_pipe' is invalid in OpenCL}}
// expected-note@-1{{'commit_read_pipe' declared here}}
commit_write_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'commit_write_pipe' is invalid in OpenCL}}
// expected-note@-1{{did you mean 'commit_read_pipe'?}}
sub_group_commit_write_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'sub_group_commit_write_pipe' is invalid in C99}}
sub_group_commit_read_pipe(tmp, tmp); // expected-warning{{implicit declaration of function 'sub_group_commit_read_pipe' is invalid in C99}}
work_group_commit_read_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'work_group_commit_read_pipe' is invalid in OpenCL}}
// expected-note@-1{{'work_group_commit_read_pipe' declared here}}
// expected-note@-2{{did you mean 'work_group_reserve_read_pipe'?}}
work_group_commit_write_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'work_group_commit_write_pipe' is invalid in OpenCL}}
// expected-note@-1{{'work_group_commit_write_pipe' declared here}}
// expected-note@-2{{did you mean 'work_group_commit_read_pipe'?}}
get_pipe_num_packets(tmp); // expected-warning{{implicit declaration of function 'get_pipe_num_packets' is invalid in C99}}
get_pipe_max_packets(tmp); // expected-warning{{implicit declaration of function 'get_pipe_max_packets' is invalid in C99}}
sub_group_commit_write_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'sub_group_commit_write_pipe' is invalid in OpenCL}}
// expected-note@-1{{did you mean 'work_group_commit_write_pipe'?}}
sub_group_commit_read_pipe(tmp, tmp); // expected-error{{implicit declaration of function 'sub_group_commit_read_pipe' is invalid in OpenCL}}
get_pipe_num_packets(tmp); // expected-error{{implicit declaration of function 'get_pipe_num_packets' is invalid in OpenCL}}
// expected-note@-1{{'get_pipe_num_packets' declared here}}
get_pipe_max_packets(tmp); // expected-error{{implicit declaration of function 'get_pipe_max_packets' is invalid in OpenCL}}
// expected-note@-1{{did you mean 'get_pipe_num_packets'?}}
}

View File

@ -89,7 +89,7 @@ float2 ntest04(int2 C, int2 X, float2 Y)
float2 ntest05(int2 C, int2 X, float Y)
{
return C ? X : Y; // expected-error {{cannot convert between vector values of different size ('int2' (vector of 2 'int' values) and 'float')}}
return C ? X : Y; // expected-error {{scalar operand type has greater rank than the type of the vector element. ('int2' (vector of 2 'int' values) and 'float'}}
}
char2 ntest06(int2 C, char2 X, char2 Y)

View File

@ -10,7 +10,7 @@ void test(void) {
glob = to_global(glob, loc);
#if __OPENCL_C_VERSION__ < CL_VERSION_2_0
// expected-warning@-2{{implicit declaration of function 'to_global' is invalid in C99}}
// expected-error@-2{{implicit declaration of function 'to_global' is invalid in OpenCL}}
// expected-warning@-3{{incompatible integer to pointer conversion assigning to '__global int *' from 'int'}}
#else
// expected-error@-5{{invalid number of arguments to function: 'to_global'}}

View File

@ -1742,6 +1742,8 @@ int perform_test_load_source(int argc, const char **argv,
return -1;
if (Repeats > 1) {
clang_suspendTranslationUnit(TU);
Err = clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files,
clang_defaultReparseOptions(TU));
if (Err != CXError_Success) {

View File

@ -3918,6 +3918,20 @@ void clang_disposeTranslationUnit(CXTranslationUnit CTUnit) {
}
}
unsigned clang_suspendTranslationUnit(CXTranslationUnit CTUnit) {
if (CTUnit) {
ASTUnit *Unit = cxtu::getASTUnit(CTUnit);
if (Unit && Unit->isUnsafeToFree())
return false;
Unit->ResetForParse();
return true;
}
return false;
}
unsigned clang_defaultReparseOptions(CXTranslationUnit TU) {
return CXReparse_None;
}

View File

@ -305,6 +305,7 @@ clang_remap_getFilenames
clang_remap_getNumFiles
clang_reparseTranslationUnit
clang_saveTranslationUnit
clang_suspendTranslationUnit
clang_sortCodeCompletionResults
clang_toggleCrashRecovery
clang_tokenize