diff options
author | Anton Yartsev <anton.yartsev@gmail.com> | 2014-05-19 15:04:55 +0000 |
---|---|---|
committer | Anton Yartsev <anton.yartsev@gmail.com> | 2014-05-19 15:04:55 +0000 |
commit | 76790c5cf0edc160c8dadc31ff8d6d439dc8ece1 (patch) | |
tree | c25f7904566124d84d09ef74c1d60b91e36adc5d /www/analyzer | |
parent | 323f398054bf96fb5ba3fc7c347ca615581cd037 (diff) |
[analyzer] Alpha and implicit checker lists, expand/collapse feature.
The list of alpha and the list of implicit checkers added. An ability to expand/collapse long texts added. Markup fixed.
http://reviews.llvm.org/D3457
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@209131 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'www/analyzer')
-rw-r--r-- | www/analyzer/alpha_checks.html | 848 | ||||
-rw-r--r-- | www/analyzer/available_checks.html | 38 | ||||
-rw-r--r-- | www/analyzer/checker_dev_manual.html | 6 | ||||
-rw-r--r-- | www/analyzer/content.css | 5 | ||||
-rw-r--r-- | www/analyzer/images/expandcollapse/arrows_dark.gif | bin | 0 -> 1377 bytes | |||
-rw-r--r-- | www/analyzer/images/expandcollapse/arrows_light.gif | bin | 0 -> 1376 bytes | |||
-rw-r--r-- | www/analyzer/images/expandcollapse/ellipses_dark.gif | bin | 0 -> 1360 bytes | |||
-rw-r--r-- | www/analyzer/images/expandcollapse/ellipses_light.gif | bin | 0 -> 1359 bytes | |||
-rw-r--r-- | www/analyzer/implicit_checks.html | 165 | ||||
-rw-r--r-- | www/analyzer/potential_checkers.html | 108 | ||||
-rw-r--r-- | www/analyzer/scripts/expandcollapse.js | 191 |
11 files changed, 1271 insertions, 90 deletions
diff --git a/www/analyzer/alpha_checks.html b/www/analyzer/alpha_checks.html new file mode 100644 index 0000000000..6f5e4f1708 --- /dev/null +++ b/www/analyzer/alpha_checks.html @@ -0,0 +1,848 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <title>Alpha Checks</title> + <link type="text/css" rel="stylesheet" href="menu.css"> + <link type="text/css" rel="stylesheet" href="content.css"> + <script type="text/javascript" src="scripts/menu.js"></script> + <script type="text/javascript" src="scripts/expandcollapse.js"></script> + <style type="text/css"> + tr:first-child { width:20%; } + </style> +</head> +<body onload="initExpandCollapse()"> + +<div id="page"> +<!--#include virtual="menu.html.incl"--> + +<div id="content"> +<h1>Alpha Checkers</h1> +Experimental checkers in addition to the <a href = "available_checks.html"> +Default Checkers</a>. These are checkers with known issues or limitations that +keep them from being on by default. They are likely to have false positives. +Bug reports are welcome but will likely not be investigated for some time. +Patches welcome! +<ul> +<li><a href="#core_alpha_checkers">Core Alpha Checkers</a></li> +<li><a href="#cplusplus_alpha_checkers">C++ Alpha Checkers</a></li> +<li><a href="#deadcode_alpha_checkers">Dead Code Alpha Checkers</a></li> +<li><a href="#osx_alpha_checkers">OS X Alpha Checkers</a></li> +<li><a href="#security_alpha_checkers">Security Alpha Checkers</a></li> +<li><a href="#unix_alpha_checkers">Unix Alpha Checkers</a></li> +</ul> + +<!------------------------------ core alpha -----------------------------------> +<h3 id="core_alpha_checkers">Core Alpha Checkers</h3> +<table class="checkers"> +<colgroup><col class="namedescr"><col class="example"></colgroup> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> + +<tbody> +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.core.BoolAssignment</span><span class="lang"> +(ObjC)</span><div class="descr"> +Warn about assigning non-{0,1} values to boolean variables.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + BOOL b = -1; // warn +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.core.CastSize</span><span class="lang"> +(C)</span><div class="descr"> +Check when casting a malloc'ed type T, whether the size is a multiple of the +size of T (Works only with <span class="name">unix.Malloc</span> +or <span class="name">alpha.unix.MallocWithAnnotations</span> +checks enabled).</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + int *x = (int *)malloc(11); // warn +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.core.CastToStruct</span><span class="lang"> +(C, C++)</span><div class="descr"> +Check for cast from non-struct pointer to struct pointer.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +// C +struct s {}; + +void test(int *p) { + struct s *ps = (struct s *) p; // warn +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +// C++ +class c {}; + +void test(int *p) { + c *pc = (c *) p; // warn +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.core.FixedAddr</span><span class="lang"> +(C)</span><div class="descr"> +Check for assignment of a fixed address to a pointer.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + int *p; + p = (int *) 0x10000; // warn +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.core.IdenticalExpr</span><span class="lang"> +(C, C++)</span><div class="descr"> +Warn about suspicious uses of identical expressions.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +// C +void test() { + int a = 5; + int b = a | 4 | a; // warn: identical expr on both sides +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +// C++ +bool f(void); + +void test(bool b) { + int i = 10; + if (f()) { // warn: true and false branches are identical + do { + i--; + } while (f()); + } else { + do { + i--; + } while (f()); + } +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.core.PointerArithm</span><span class="lang"> +(C)</span><div class="descr"> +Check for pointer arithmetic on locations other than array +elements.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + int x; + int *p; + p = &x + 1; // warn +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.core.PointerSub</span><span class="lang"> +(C)</span><div class="descr"> +Check for pointer subtractions on two pointers pointing to different memory +chunks.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + int x, y; + int d = &y - &x; // warn +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.core.SizeofPtr</span><span class="lang"> +(C)</span><div class="descr"> +Warn about unintended use of <code>sizeof()</code> on pointer +expressions.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +struct s {}; + +int test(struct s *p) { + return sizeof(p); + // warn: sizeof(ptr) can produce an unexpected result +} +</pre></div></div></td></tr> + +</tbody></table> + +<!--------------------------- cplusplus alpha ---------------------------------> +<h3 id="cplusplus_alpha_checkers">C++ Alpha Checkers</h3> +<table class="checkers"> +<colgroup><col class="namedescr"><col class="example"></colgroup> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> + +<tbody> +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.cplusplus.NewDeleteLeaks</span><span class="lang"> +(C++)</span><div class="descr"> +Check for memory leaks. Traces memory managed by <code>new</code>/<code> +delete</code>.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + int *p = new int; +} // warn +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.cplusplus.VirtualCall</span><span class="lang"> +(C++)</span><div class="descr"> +Check virtual member function calls during construction or +destruction.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +class A { +public: + A() { + f(); // warn + } + virtual void f(); +}; +</pre></div><div class="separator"></div> +<div class="example"><pre> +class A { +public: + ~A() { + this->f(); // warn + } + virtual void f(); +}; +</pre></div></div></td></tr> + +</tbody></table> + +<!--------------------------- dead code alpha ---------------------------------> +<h3 id="deadcode_alpha_checkers">Dead Code Alpha Checkers</h3> +<table class="checkers"> +<colgroup><col class="namedescr"><col class="example"></colgroup> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> + +<tbody> +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.deadcode.UnreachableCode</span><span class="lang"> +(C, C++, ObjC)</span><div class="descr"> +Check unreachable code.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +// C +int test() { + int x = 1; + while(x); + return x; // warn +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +// C++ +void test() { + int a = 2; + + while (a > 1) + a--; + + if (a > 1) + a++; // warn +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +// Objective-C +void test(id x) { + return; + [x retain]; // warn +} +</pre></div></div></td></tr> +</tbody></table> + +<!---------------------------- OS X alpha --------------------------------------> +<h3 id="osx_alpha_checkers">OS X Alpha Checkers</h3> +<table class="checkers"> +<colgroup><col class="namedescr"><col class="example"></colgroup> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> + +<tbody> +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.osx.cocoa.Dealloc</span><span class="lang"> +(ObjC)</span><div class="descr"> +Warn about Objective-C classes that lack a correct implementation +of <code>-dealloc</code>. +</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +@interface MyObject : NSObject { + id _myproperty; +} +@end + +@implementation MyObject // warn: lacks 'dealloc' +@end +</pre></div><div class="separator"></div> +<div class="example"><pre> +@interface MyObject : NSObject {} +@property(assign) id myproperty; +@end + +@implementation MyObject // warn: does not send 'dealloc' to super +- (void)dealloc { + self.myproperty = 0; +} +@end +</pre></div><div class="separator"></div> +<div class="example"><pre> +@interface MyObject : NSObject { + id _myproperty; +} +@property(retain) id myproperty; +@end + +@implementation MyObject +@synthesize myproperty = _myproperty; + // warn: var was retained but wasn't released +- (void)dealloc { + [super dealloc]; +} +@end +</pre></div><div class="separator"></div> +<div class="example"><pre> +@interface MyObject : NSObject { + id _myproperty; +} +@property(assign) id myproperty; +@end + +@implementation MyObject +@synthesize myproperty = _myproperty; + // warn: var wasn't retained but was released +- (void)dealloc { + [_myproperty release]; + [super dealloc]; +} +@end +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.osx.cocoa.DirectIvarAssignment</span><span class="lang"> +(ObjC)</span><div class="descr"> +Check that Objective C properties follow the following rule: the property +should be set with the setter, not though a direct assignment.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +@interface MyClass : NSObject {} +@property (readonly) id A; +- (void) foo; +@end + +@implementation MyClass +- (void) foo { + _A = 0; // warn +} +@end +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.osx.cocoa.DirectIvarAssignmentForAnnotatedFunctions</span><span class="lang"> +(ObjC)</span><div class="descr"> +Check for direct assignments to instance variables in the methods annotated +with <code>objc_no_direct_instance_variable_assignment</code>.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +@interface MyClass : NSObject {} +@property (readonly) id A; +- (void) fAnnotated __attribute__(( + annotate("objc_no_direct_instance_variable_assignment"))); +- (void) fNotAnnotated; +@end + +@implementation MyClass +- (void) fAnnotated { + _A = 0; // warn +} +- (void) fNotAnnotated { + _A = 0; // no warn +} +@end +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.osx.cocoa.InstanceVariableInvalidation</span><span class="lang"> +(ObjC)</span><div class="descr"> +Check that the invalidatable instance variables are invalidated in the methods +annotated with <code>objc_instance_variable_invalidator</code>.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +@protocol Invalidation <NSObject> +- (void) invalidate + __attribute__((annotate("objc_instance_variable_invalidator"))); +@end + +@interface InvalidationImpObj : NSObject <Invalidation> +@end + +@interface SubclassInvalidationImpObj : InvalidationImpObj { + InvalidationImpObj *var; +} +- (void)invalidate; +@end + +@implementation SubclassInvalidationImpObj +- (void) invalidate {} +@end +// warn: var needs to be invalidated or set to nil +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.osx.cocoa.MissingInvalidationMethod</span><span class="lang"> +(ObjC)</span><div class="descr"> +Check that the invalidation methods are present in classes that contain +invalidatable instance variables.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +@protocol Invalidation <NSObject> +- (void)invalidate + __attribute__((annotate("objc_instance_variable_invalidator"))); +@end + +@interface NeedInvalidation : NSObject <Invalidation> +@end + +@interface MissingInvalidationMethodDecl : NSObject { + NeedInvalidation *Var; // warn +} +@end + +@implementation MissingInvalidationMethodDecl +@end +</pre></div></div></td></tr> + +</tbody></table> + +<!------------------------- security alpha ------------------------------------> +<h3 id="security_alpha_checkers">Security Alpha Checkers</h3> +<table class="checkers"> +<colgroup><col class="namedescr"><col class="example"></colgroup> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> + +<tbody> +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.security.ArrayBound</span><span class="lang"> +(C)</span><div class="descr"> +Warn about buffer overflows (older checker).</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + char *s = ""; + char c = s[1]; // warn +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +struct seven_words { + int c[7]; +}; + +void test() { + struct seven_words a, *p; + p = &a; + p[0] = a; + p[1] = a; + p[2] = a; // warn +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +// note: requires unix.Malloc or +// alpha.unix.MallocWithAnnotations checks enabled. +void test() { + int *p = malloc(12); + p[3] = 4; // warn +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +void test() { + char a[2]; + int *b = (int*)a; + b[1] = 3; // warn +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.security.ArrayBoundV2</span><span class="lang"> +(C)</span><div class="descr"> +Warn about buffer overflows (newer checker).</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + char *s = ""; + char c = s[1]; // warn +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +void test() { + int buf[100]; + int *p = buf; + p = p + 99; + p[1] = 1; // warn +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +// note: compiler has internal check for this. +// Use -Wno-array-bounds to suppress compiler warning. +void test() { + int buf[100][100]; + buf[0][-1] = 1; // warn +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +// note: requires alpha.security.taint check turned on. +void test() { + char s[] = "abc"; + int x = getchar(); + char c = s[x]; // warn: index is tainted +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.security.MallocOverflow</span><span class="lang"> +(C)</span><div class="descr"> +Check for overflows in the arguments to <code>malloc()</code>.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test(int n) { + void *p = malloc(n * sizeof(int)); // warn +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.security.ReturnPtrRange</span><span class="lang"> +(C)</span><div class="descr"> +Check for an out-of-bound pointer being returned to callers.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +static int A[10]; + +int *test() { + int *p = A + 10; + return p; // warn +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +int test(void) { + int x; + return x; // warn: undefined or garbage returned +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.security.taint.TaintPropagation</span><span class="lang"> +(C)</span><div class="descr"> +Generate taint information used by other checkers.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + char x = getchar(); // 'x' marked as tainted + system(&x); // warn: untrusted data is passed to a system call +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +// note: compiler internally checks if the second param to +// sprintf is a string literal or not. +// Use -Wno-format-security to suppress compiler warning. +void test() { + char s[10], buf[10]; + fscanf(stdin, "%s", s); // 's' marked as tainted + + sprintf(buf, s); // warn: untrusted data as a format string +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +void test() { + size_t ts; + scanf("%zd", &ts); // 'ts' marked as tainted + int *p = (int *)malloc(ts * sizeof(int)); + // warn: untrusted data as bufer size +} +</pre></div></div></td></tr> + +</tbody></table> + +<!--------------------------- unix alpha --------------------------------------> +<h3 id="unix_alpha_checkers">Unix Alpha Checkers</h3> +<table class="checkers"> +<colgroup><col class="namedescr"><col class="example"></colgroup> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> + +<tbody> +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.unix.Chroot</span><span class="lang"> +(C)</span><div class="descr"> +Check improper use of <code>chroot</code>.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void f(); + +void test() { + chroot("/usr/local"); + f(); // warn: no call of chdir("/") immediately after chroot +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.unix.MallocWithAnnotations</span><span class="lang"> +(C)</span><div class="descr"> +Check for memory leaks, double free, and use-after-free problems. Assumes that +all user-defined functions which might free a pointer are +annotated.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void __attribute((ownership_returns(malloc))) *my_malloc(size_t); + +void test() { + int *p = my_malloc(1); +} // warn: potential leak +</pre></div><div class="separator"></div> +<div class="example"><pre> +void __attribute((ownership_returns(malloc))) *my_malloc(size_t); +void __attribute((ownership_takes(malloc, 1))) my_free(void *); + +void test() { + int *p = my_malloc(1); + my_free(p); + my_free(p); // warn: attempt to free released +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +void __attribute((ownership_returns(malloc))) *my_malloc(size_t); +void __attribute((ownership_holds(malloc, 1))) my_hold(void *); + +void test() { + int *p = my_malloc(1); + my_hold(p); + free(p); // warn: attempt to free non-owned memory +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +void __attribute((ownership_takes(malloc, 1))) my_free(void *); + +void test() { + int *p = malloc(1); + my_free(p); + *p = 1; // warn: use after free +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.unix.PthreadLock</span><span class="lang"> +(C)</span><div class="descr"> +Simple lock -> unlock checker; applies to:<div class=functions> +pthread_mutex_lock<br> +pthread_rwlock_rdlock<br> +pthread_rwlock_wrlock<br> +lck_mtx_lock<br> +lck_rw_lock_exclusive<br> +lck_rw_lock_shared<br> +pthread_mutex_trylock<br> +pthread_rwlock_tryrdlock<br> +pthread_rwlock_tryrwlock<br> +lck_mtx_try_lock<br> +lck_rw_try_lock_exclusive<br> +lck_rw_try_lock_shared<br> +pthread_mutex_unlock<br> +pthread_rwlock_unlock<br> +lck_mtx_unlock<br> +lck_rw_done</div></div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +pthread_mutex_t mtx; + +void test() { + pthread_mutex_lock(&mtx); + pthread_mutex_lock(&mtx); + // warn: this lock has already been acquired +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +lck_mtx_t lck1, lck2; + +void test() { + lck_mtx_lock(&lck1); + lck_mtx_lock(&lck2); + lck_mtx_unlock(&lck1); + // warn: this was not the most recently acquired lock +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +lck_mtx_t lck1, lck2; + +void test() { + if (lck_mtx_try_lock(&lck1) == 0) + return; + + lck_mtx_lock(&lck2); + lck_mtx_unlock(&lck1); + // warn: this was not the most recently acquired lock +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.unix.SimpleStream</span><span class="lang"> +(C)</span><div class="descr"> +Check for misuses of stream APIs:<div class=functions> +fopen<br> +fclose</div>(demo checker, the subject of the demo +(<a href="http://llvm.org/devmtg/2012-11/Zaks-Rose-Checker24Hours.pdf">Slides</a> +,<a href="http://llvm.org/devmtg/2012-11/videos/Zaks-Rose-Checker24Hours.mp4">Video</a>) +by Anna Zaks and Jordan Rose presented at the <a href="http://llvm.org/devmtg/2012-11/"> +2012 LLVM Developers' Meeting).</a></div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + FILE *F = fopen("myfile.txt", "w"); +} // warn: opened file is never closed +</pre></div><div class="separator"></div> +<div class="example"><pre> +void test() { + FILE *F = fopen("myfile.txt", "w"); + + if (F) + fclose(F); + + fclose(F); // warn: closing a previously closed file stream +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.unix.Stream</span><span class="lang"> +(C)</span><div class="descr"> +Check stream handling functions:<div class=functions>fopen<br> +tmpfile<br> +fclose<br> +fread<br> +fwrite<br> +fseek<br> +ftell<br> +rewind<br> +fgetpos<br> +fsetpos<br> +clearerr<br> +feof<br> +ferror<br> +fileno</div></div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + FILE *p = fopen("foo", "r"); +} // warn: opened file is never closed +</pre></div><div class="separator"></div> +<div class="example"><pre> +void test() { + FILE *p = fopen("foo", "r"); + fseek(p, 1, SEEK_SET); // warn: stream pointer might be NULL + fclose(p); +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +void test() { + FILE *p = fopen("foo", "r"); + + if (p) + fseek(p, 1, 3); + // warn: third arg should be SEEK_SET, SEEK_END, or SEEK_CUR + + fclose(p); +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +void test() { + FILE *p = fopen("foo", "r"); + fclose(p); + fclose(p); // warn: already closed +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +void test() { + FILE *p = tmpfile(); + ftell(p); // warn: stream pointer might be NULL + fclose(p); +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.unix.cstring.BufferOverlap</span><span class="lang"> +(C)</span><div class="descr"> +Checks for overlap in two buffer arguments; applies to:<div class=functions> +memcpy<br> +mempcpy</div></div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + int a[4] = {0}; + memcpy(a + 2, a + 1, 8); // warn +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.unix.cstring.NotNullTerminated</span><span class="lang"> +(C)</span><div class="descr"> +Check for arguments which are not null-terminated strings; applies +to:<div class=functions> +strlen<br> +strnlen<br> +strcpy<br> +strncpy<br> +strcat<br> +strncat</div></div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + int y = strlen((char *)&test); // warn +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +alpha.unix.cstring.OutOfBounds</span><span class="lang"> +(C)</span><div class="descr"> +Check for out-of-bounds access in string functions; applies +to:<div class=functions> +strncopy<br> +strncat</div></div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test(char *y) { + char x[4]; + if (strlen(y) == 4) + strncpy(x, y, 5); // warn +} +</pre></div></div></td></tr> + +</tbody></table> + +</div> <!-- page --> +</div> <!-- content --> +</body> +</html> diff --git a/www/analyzer/available_checks.html b/www/analyzer/available_checks.html index 1f4625ee94..7707fc0150 100644 --- a/www/analyzer/available_checks.html +++ b/www/analyzer/available_checks.html @@ -47,15 +47,9 @@ Experimental (Alpha) Checkers</a>. <h3 id="core_checkers">Core Checkers</h3> <table class="checkers"> <colgroup><col class="namedescr"><col class="example"></colgroup> - -<thead> -<tr><td><div class="namedescr">Name, Description</div></td> -<td><div class="example">Example</div></td></tr> -</thead> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> <tbody> - - <tr><td><div class="namedescr expandable"><span class="name"> core.CallAndMessage</span><span class="lang"> (C, C++, ObjC)</span><div class="descr"> @@ -370,11 +364,7 @@ int test() { <h3 id="cplusplus_checkers">C++ Checkers</h3> <table class="checkers"> <colgroup><col class="namedescr"><col class="example"></colgroup> - -<thead> -<tr><td><div class="namedescr">Name, Description</div></td> -<td><div class="example">Example</div></td></tr> -</thead> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> <tbody> <tr><td><div class="namedescr expandable"><span class="name"> @@ -437,11 +427,7 @@ void test() { <h3 id="deadcode_checkers">Dead Code Checkers</h3> <table class="checkers"> <colgroup><col class="namedescr"><col class="example"></colgroup> - -<thead> -<tr><td><div class="namedescr">Name, Description</div></td> -<td><div class="example">Example</div></td></tr> -</thead> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> <tbody> <tr><td><div class="namedescr expandable"><span class="name"> @@ -462,11 +448,7 @@ void test() { <h3 id="osx_checkers">OS X Checkers</h3> <table class="checkers"> <colgroup><col class="namedescr"><col class="example"></colgroup> - -<thead> -<tr><td><div class="namedescr">Name, Description</div></td> -<td><div class="example">Example</div></td></tr> -</thead> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> <tbody> <tr><td><div class="namedescr expandable"><span class="name"> @@ -877,11 +859,7 @@ void test() { <h3 id="security_checkers">Security Checkers</h3> <table class="checkers"> <colgroup><col class="namedescr"><col class="example"></colgroup> - -<thead> -<tr><td><div class="namedescr">Name, Description</div></td> -<td><div class="example">Example</div></td></tr> -</thead> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> <tbody> <tr><td><div class="namedescr expandable"><span class="name"> @@ -1021,11 +999,7 @@ void test() { <h3 id="unix_checkers">Unix Checkers</h3> <table class="checkers"> <colgroup><col class="namedescr"><col class="example"></colgroup> - -<thead> -<tr><td><div class="namedescr">Name, Description</div></td> -<td><div class="example">Example</div></td></tr> -</thead> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> <tbody> <tr><td><div class="namedescr expandable"><span class="name"> diff --git a/www/analyzer/checker_dev_manual.html b/www/analyzer/checker_dev_manual.html index 2216176a13..9c7cd98efa 100644 --- a/www/analyzer/checker_dev_manual.html +++ b/www/analyzer/checker_dev_manual.html @@ -47,6 +47,7 @@ for developer guidelines and send your questions and proposals to <li><a href="#testing">Testing</a></li> <li><a href="#commands">Useful Commands/Debugging Hints</a></li> <li><a href="#additioninformation">Additional Sources of Information</a></li> + <li><a href="#links">Useful Links</a></li> </ul> <h2 id=start>Getting Started</h2> @@ -605,6 +606,11 @@ href="http://llvm.org/devmtg/2012-11/videos/Zaks-Rose-Checker24Hours.mp4">video< are available. </ul> +<h2 id=links>Useful Links</h2> +<ul> +<li>The list of <a href="implicit_checks.html">Implicit Checkers</a></li> +</ul> + </div> </div> </body> diff --git a/www/analyzer/content.css b/www/analyzer/content.css index 67a9e6d69e..c4d601b4b1 100644 --- a/www/analyzer/content.css +++ b/www/analyzer/content.css @@ -84,16 +84,17 @@ table.checkers td.aligned { text-align: center; vertical-align: middle; } table.checkers col.namedescr { width: 45% } table.checkers col.example { width: 55% } table.checkers col.progress { width: 84px } -table.checkers thead div.example, +table.checkers thead td, table.checkers div.namedescr, table.checkers div.exampleContainer { overflow: hidden; padding: 5px 8px 10px 8px } /* table.checkers tbody div.example { font-family: monospace; white-space: pre } */ table.checkers div.example { border-top:1px #cccccc dashed; width:100%; padding-top: 5px; margin-top: 5px } -table.checkers tbody div.example:first-child { border-top:none; padding-top: 0px; margin-top: 0px } +table.checkers div.example:first-child { border-top:none; padding-top: 0px; margin-top: 0px } table.checkers span.name { font-weight: bold } table.checkers span.lang { font-weight: bold; padding-left: 7px; /* display:block; */ } table.checkers div.descr { margin-top:7px } table.checkers div.functions { margin-top: 2px; font-style: italic; font-size: 90%; color:#00B } table.checkers pre { margin: 1px; font-size: 100%; word-wrap: break-word } table.checkers p { margin: 10px 0px 0px 0px; } +table.checkers ul, li { margin: 0 } diff --git a/www/analyzer/images/expandcollapse/arrows_dark.gif b/www/analyzer/images/expandcollapse/arrows_dark.gif Binary files differnew file mode 100644 index 0000000000..445a3c5533 --- /dev/null +++ b/www/analyzer/images/expandcollapse/arrows_dark.gif diff --git a/www/analyzer/images/expandcollapse/arrows_light.gif b/www/analyzer/images/expandcollapse/arrows_light.gif Binary files differnew file mode 100644 index 0000000000..4534d05af9 --- /dev/null +++ b/www/analyzer/images/expandcollapse/arrows_light.gif diff --git a/www/analyzer/images/expandcollapse/ellipses_dark.gif b/www/analyzer/images/expandcollapse/ellipses_dark.gif Binary files differnew file mode 100644 index 0000000000..8ed43b0e7d --- /dev/null +++ b/www/analyzer/images/expandcollapse/ellipses_dark.gif diff --git a/www/analyzer/images/expandcollapse/ellipses_light.gif b/www/analyzer/images/expandcollapse/ellipses_light.gif Binary files differnew file mode 100644 index 0000000000..90141ba852 --- /dev/null +++ b/www/analyzer/images/expandcollapse/ellipses_light.gif diff --git a/www/analyzer/implicit_checks.html b/www/analyzer/implicit_checks.html new file mode 100644 index 0000000000..a476c9bf32 --- /dev/null +++ b/www/analyzer/implicit_checks.html @@ -0,0 +1,165 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <title>Implicit Checks</title> + <link type="text/css" rel="stylesheet" href="menu.css"> + <link type="text/css" rel="stylesheet" href="content.css"> + <script type="text/javascript" src="scripts/menu.js"></script> + <script type="text/javascript" src="scripts/expandcollapse.js"></script> + <style type="text/css"> + tr:first-child { width:20%; } + </style> +</head> +<body onload="initExpandCollapse()"> + +<div id="page"> +<!--#include virtual="menu.html.incl"--> + +<div id="content"> +<h1>Implicit Checkers</h1> +Even though the implicit checkers do not produce any warnings, they are used to +support the analyzer core and model known APIs. See also +<a href = "available_checks.html">Default Checkers</a> +and <a href = "alpha_checks.html">Experimental (Alpha) Checkers</a>. +<ul> +<li><a href="#core_implicit_checkers">Core Implicit Checkers</a></li> +<li><a href="#osx_implicit_checkers">OS X Implicit Checkers</a></li> +</ul> + +<!------------------------------- core implicit -------------------------------> +<h3 id="core_implicit_checkers">Core Implicit Checkers</h3> +<table class="checkers"> +<colgroup><col class="namedescr"><col class="example"></colgroup> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> + +<tbody> +<tr><td><div class="namedescr expandable"><span class="name"> +core.DynamicTypePropagation</span><span class="lang"> +(C++, ObjC)</span><div class="descr"> +Generate dynamic type information.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +// C++ +class A { +public: + A(int x) {} + virtual int foo(); +}; + +class B: public A { +public: + B() + :A(foo()) + // DynamicTypeInfo for 'this' rigion will wrap type 'A' + // unless the base constructor call expression is processed + {} + virtual int foo(); +}; +</pre></div><div class="separator"></div> +<div class="example"><pre> +// Objective-C +@interface MyClass : NSObject {} +@end + +@implementation MyClass ++ (void)foo { + MyClass *x = [[self alloc] init]; + // DynamicTypeInfo from a cast: from 'id' to 'MyClass *' +} +@end + +void test() { + MyClass *x = [MyClass alloc]; + // DynamicTypeInfo from a call to alloc: + // from 'id' to 'MyClass *' +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +core.builtin.BuiltinFunctions</span><span class="lang"> +(C)</span><div class="descr"> +Evaluate compiler builtin functions (e.g., <code>alloca()</code>)</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test(int x) { + int *p = (int *)__builtin_alloca(8); + // evaluates to AllocaRegion + + if(__builtin_expect(x > 10, 0)) // evaluates to 'x > 10' + x = 0; +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +core.builtin.NoReturnFunctions</span><span class="lang"> +(C, ObjC)</span><div class="descr"> +Evaluate "panic" functions that are known to not return to the caller.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +// C +void test() { + panic(); // generate sink +} +</pre></div><div class="separator"></div> +<div class="example"><pre> +// Objective-C +@interface MyObj : NSObject {} +- (void)foo; +@end + +@implementation MyObj +- (void)foo { + [[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd + object:self + file:(@"somefile.m") + lineNumber:1 + description:(@"some text")]; + // generate sink +} +@end +</pre></div></div></td></tr> + +</tbody></table> + +<!---------------------------- OS X implicit ----------------------------------> +<h3 id="osx_implicit_checkers">OS X Implicit Checkers</h3> +<table class="checkers"> +<colgroup><col class="namedescr"><col class="example"></colgroup> +<thead><tr><td>Name, Description</td><td>Example</td></tr></thead> + +<tbody> +<tr><td><div class="namedescr expandable"><span class="name"> +osx.cocoa.Loops</span><span class="lang"> +(ObjC)</span><div class="descr"> +Improved modeling of loops using Cocoa collection types.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test() { + id x; + for (x in [NSArray testObject]) { + // assume the value of 'x' is non-nil + } +} +</pre></div></div></td></tr> + + +<tr><td><div class="namedescr expandable"><span class="name"> +osx.cocoa.NonNilReturnValue</span><span class="lang"> +(ObjC)</span><div class="descr"> +Model the APIs that are guaranteed to return a non-nil value.</div></div></td> +<td><div class="exampleContainer expandable"> +<div class="example"><pre> +void test(NSArray *A) { + id subscriptObj = A[1]; // assume the value is non-nil +} +</pre></div></div></td></tr> + +</tbody></table> + +</div> <!-- page --> +</div> <!-- content --> +</body> +</html> diff --git a/www/analyzer/potential_checkers.html b/www/analyzer/potential_checkers.html index a6c568f798..a06e9426fb 100644 --- a/www/analyzer/potential_checkers.html +++ b/www/analyzer/potential_checkers.html @@ -5,9 +5,10 @@ <title>List of potential checkers</title> <link type="text/css" rel="stylesheet" href="content.css"> <link type="text/css" rel="stylesheet" href="menu.css"> + <script type="text/javascript" src="scripts/expandcollapse.js"></script> <script type="text/javascript" src="scripts/menu.js"></script> </head> -<body> +<body onload="initExpandCollapse()"> <div id="page"> @@ -470,11 +471,22 @@ int i = *p; // warn <td class="aligned"></td></tr> -<!--<tr><td><div class="namedescr expandable"><span class="name"> +<tr><td><div class="namedescr expandable"><span class="name"> undefbehavior.DeadReferenced</span><span class="lang"> (C++)</span><div class="descr"> -Undefined behavior: the following usage of the pointer to the object whose -lifetime has ended can result in undefined behavior. +Undefined behavior: the following usage of the pointer to the object whose +lifetime has ended can result in undefined behavior:<br> +The object will be or was of a class type with a non-trivial destructor and +<ul><li>the pointer is used as the operand of a delete-expression</li></ul> +The object will be or was of a non-POD class type (C++11: any class type) and +<ul><li>the pointer is used to access a non-static data member or call a +non-static member function of the object</li> +<li>the pointer is implicitly converted to a pointer to a base class +type</li> +<li>the pointer is used as the operand of a <code>static_cast</code> (except +when the conversion is to <code>void*</code>, or to <code>void*</code> and +subsequently to <code>char*</code>, or <code>unsigned char*</code>)</li> +<li>the pointer is used as the operand of a <code>dynamic_cast</code></li></ul> <p>Source: C++03 3.8p5, p7; C++11 3.8p5, p7.</p></div></div></td> <td><div class="exampleContainer expandable"> <div class="example"><pre> @@ -482,15 +494,15 @@ lifetime has ended can result in undefined behavior. class A { public: - int i; + ~A(); }; class B : public A {}; -int test() { - B *b = new B; - new(b) A; - return b->i; // warn +void test() { + A *a = new A; + new(a) B; + delete a; // warn } </pre></div> <div class="example"><pre> @@ -498,90 +510,74 @@ int test() { class A { public: - void f() {}; + ~A(); }; -class B : public A {}; +class B {}; void test() { - B *b = new B; - new(b) A; - b->f(); // warn + A *a = new A; + new(a) B; + a->~A(); } </pre></div> <div class="example"><pre> #include <new> -class A {}; +class A { +public: + ~A(); +}; class B : public A {}; -A* test() { - B *b = new B; - new(b) A; - static_cast<A*>(b); // warn -} -</pre></div> -<div class="example"><pre> -#include <new> - -class A {}; +class C {}; -class B : public A {}; +void f(A*); -A* test() { +void test() { B *b = new B; - new(b) A; - dynamic_cast<A*>(b); // warn + new(b) C; + f(b); // warn } </pre></div> <div class="example"><pre> #include <new> -class A {}; +class A { +public: + ~A(); +}; class B : public A {}; +class C {}; + A* test() { B *b = new B; - new(b) A; - dynamic_cast<A*>(b); // warn + new(b) C; + return static_cast<A*>(b); // warn } </pre></div> <div class="example"><pre> #include <new> -class A {}; - -class B : public A {}; - -void test() { - B *b = new B; - new(b) A; - delete b; // warn -} -</pre></div> -<div class="example"><pre> -// C++11 -#include <new> - class A { public: - int i; + ~A(); }; -class B : public A { -public: - ~B() {}; -}; +class B : public A {}; -int test() { - A *a = new A; - new(a) B; - return a->i; // warn +class C {}; + +A* test() { + B *b = new B; + new(b) C; + return dynamic_cast<A*>(b); // warn } </pre></div></div></td> -<td class="aligned"></td></tr>--> +<td class="aligned"></td></tr> <tr><td><div class="namedescr expandable"><span class="name"> diff --git a/www/analyzer/scripts/expandcollapse.js b/www/analyzer/scripts/expandcollapse.js new file mode 100644 index 0000000000..593a9831c8 --- /dev/null +++ b/www/analyzer/scripts/expandcollapse.js @@ -0,0 +1,191 @@ +// expand/collapse button (expander) is added if height of a cell content +// exceeds CLIP_HEIGHT px. +var CLIP_HEIGHT = 135; + +// Height in pixels of an expander image. +var EXPANDER_HEIGHT = 13; + +// Path to images for an expander. +var imgPath = "./images/expandcollapse/"; + +// array[group][cell] of { 'height', 'expanded' }. +// group: a number; cells of the same group belong to the same table row. +// cell: a number; unique index of a cell in a group. +// height: a number, px; original height of a cell in a table. +// expanded: boolean; is a cell expanded or collapsed? +var CellsInfo = []; + +// Extracts group and cell indices from an id of the form identifier_group_cell. +function getCellIdx(id) { + var idx = id.substr(id.indexOf("_") + 1).split("_"); + return { 'group': idx[0], 'cell': idx[1] }; +} + +// Returns { 'height', 'expanded' } info for a cell with a given id. +function getCellInfo(id) { + var idx = getCellIdx(id); + return CellsInfo[idx.group][idx.cell]; +} + +// Initialization, add nodes, collect info. +function initExpandCollapse() { + if (!document.getElementById) + return; + + var groupCount = 0; + + // Examine all table rows in the document. + var rows = document.body.getElementsByTagName("tr"); + for (var i=0; i<rows.length; i+=1) { + + var cellCount=0, newGroupCreated = false; + + // Examine all divs in a table row. + var divs = rows[i].getElementsByTagName("div"); + for (var j=0; j<divs.length; j+=1) { + + var expandableDiv = divs[j]; + + if (expandableDiv.className.indexOf("expandable") == -1) + continue; + + if (expandableDiv.offsetHeight <= CLIP_HEIGHT) + continue; + + // We found a div wrapping a cell content whose height exceeds + // CLIP_HEIGHT. + var originalHeight = expandableDiv.offsetHeight; + // Unique postfix for ids for generated nodes for a given cell. + var idxStr = "_" + groupCount + "_" + cellCount; + // Create an expander and an additional wrapper for a cell content. + // + // --- expandableDiv ---- + // --- expandableDiv --- | ------ data ------ | + // | cell content | -> | | cell content | | + // --------------------- | ------------------ | + // | ---- expander ---- | + // ---------------------- + var data = document.createElement("div"); + data.className = "data"; + data.id = "data" + idxStr; + data.innerHTML = expandableDiv.innerHTML; + with (data.style) { height = (CLIP_HEIGHT - EXPANDER_HEIGHT) + "px"; + overflow = "hidden" } + + var expander = document.createElement("img"); + with (expander.style) { display = "block"; paddingTop = "5px"; } + expander.src = imgPath + "ellipses_light.gif"; + expander.id = "expander" + idxStr; + + // Add mouse calbacks to expander. + expander.onclick = function() { + expandCollapse(this.id); + // Hack for Opera - onmouseout callback is not invoked when page + // content changes dinamically and mouse pointer goes out of an element. + this.src = imgPath + + (getCellInfo(this.id).expanded ? "arrows_light.gif" + : "ellipses_light.gif"); + } + expander.onmouseover = function() { + this.src = imgPath + + (getCellInfo(this.id).expanded ? "arrows_dark.gif" + : "ellipses_dark.gif"); + } + expander.onmouseout = function() { + this.src = imgPath + + (getCellInfo(this.id).expanded ? "arrows_light.gif" + : "ellipses_light.gif"); + } + + expandableDiv.innerHTML = ""; + expandableDiv.appendChild(data); + expandableDiv.appendChild(expander); + expandableDiv.style.height = CLIP_HEIGHT + "px"; + expandableDiv.id = "cell"+ idxStr; + + // Keep original cell height and its ecpanded/cpllapsed state. + if (!newGroupCreated) { + CellsInfo[groupCount] = []; + newGroupCreated = true; + } + CellsInfo[groupCount][cellCount] = { 'height' : originalHeight, + 'expanded' : false }; + cellCount += 1; + } + groupCount += newGroupCreated ? 1 : 0; + } +} + +function isElemTopVisible(elem) { + var body = document.body, + html = document.documentElement, + // Calculate expandableDiv absolute Y coordinate from the top of body. + bodyRect = body.getBoundingClientRect(), + elemRect = elem.getBoundingClientRect(), + elemOffset = Math.floor(elemRect.top - bodyRect.top), + // Calculate the absoute Y coordinate of visible area. + scrollTop = html.scrollTop || body && body.scrollTop || 0; + scrollTop -= html.clientTop; // IE<8 + + + if (elemOffset < scrollTop) + return false; + + return true; +} + +// Invoked when an expander is pressed; expand/collapse a cell. +function expandCollapse(id) { + var cellInfo = getCellInfo(id); + var idx = getCellIdx(id); + + // New height of a row. + var newHeight; + // Smart page scrolling may be done after collapse. + var mayNeedScroll; + + if (cellInfo.expanded) { + // Cell is expanded - collapse the row height to CLIP_HEIGHT. + newHeight = CLIP_HEIGHT; + mayNeedScroll = true; + } + else { + // Cell is collapsed - expand the row height to the cells original height. + newHeight = cellInfo.height; + mayNeedScroll = false; + } + + // Update all cells (height and expanded/collapsed state) in a row according + // to the new height of the row. + for (var i = 0; i < CellsInfo[idx.group].length; i++) { + var idxStr = "_" + idx.group + "_" + i; + var expandableDiv = document.getElementById("cell" + idxStr); + expandableDiv.style.height = newHeight + "px"; + var data = document.getElementById("data" + idxStr); + var expander = document.getElementById("expander" + idxStr); + var state = CellsInfo[idx.group][i]; + + if (state.height > newHeight) { + // Cell height exceeds row height - collapse a cell. + data.style.height = (newHeight - EXPANDER_HEIGHT) + "px"; + expander.src = imgPath + "ellipses_light.gif"; + CellsInfo[idx.group][i].expanded = false; + } else { + // Cell height is less then or equal to row height - expand a cell. + data.style.height = ""; + expander.src = imgPath + "arrows_light.gif"; + CellsInfo[idx.group][i].expanded = true; + } + } + + if (mayNeedScroll) { + var idxStr = "_" + idx.group + "_" + idx.cell; + var clickedExpandableDiv = document.getElementById("cell" + idxStr); + // Scroll page up if a row is collapsed and the rows top is above the + // viewport. The amount of scroll is the difference between a new and old + // row height. + if (!isElemTopVisible(clickedExpandableDiv)) { + window.scrollBy(0, newHeight - cellInfo.height); + } + } +} |