The InspectionGadgets plugin extends the IDEA inspection system, providing over 500 new code inspections and interactive warnings. These inspections provide insight into the small-scale structure of your code, find (and in some cases automatically fix) bugs and code weaknesses before compilation, assist in the enforcement of coding standards, and enable more productive code reviews. These code inspections are available both in batch mode, via the Tools/Inspect Code... panel, and in interactive mode, as "yellow-line" warnings during editing. The inspections implemented here have been chosen from popular coding standards, as well as from existing commercially available and open-source code inspection tools. At present IDEA+InspectionGadgets is probably the most full-featured platform available for static analysis of Java and J2EE programs. Simply put, this thing will kill a lot of bugs.
(Inspections marked with an asterisk (*) have automatic quick fixes provided.)
- Abstraction issues
- Cast to a concrete class
- Chain of 'instanceof' checks
- Class references one of its subclasses
- Collection declared by class, not interface
- Feature Envy
- Instance variable of concrete class
- 'instanceof' a concrete class
- 'instanceof' check for 'this'
- Local variable of concrete class
- "Magic number"(r)
- Method parameter of concrete class
- Method return of concrete class
- Overly-strong type cast(*)
- private method only used from inner class
- Public method not exposed in interface
- Static variable of concrete class
- Assignment issues
- Assignment replaceable with operator assignment(*)
- Assignment to catch block parameter(*)
- Assignment to 'for' loop parameter
- Assignment to method parameter(*)
- Nested assignment
- Value of ++ or -- used
- Bitwise operation issues
- Incompatible bitwise mask operation
- Pointless bitwise expression(*)
- Shift operation by inappropriate constant
- Class metrics
- Anonymous inner class with too many methods(r)
- Class too deep in inheritance tree
- Class with too many constructors
- Class with too many fields
- Class with too many methods
- Inner class too deeply nested
- Overly complex anonymous inner class(r)
- Overly complex class
- Overly coupled class
- Class structure
- Anonymous inner class(r)
- Class may be interface(*)
- Class name differs from file name(*)
- Class without package statement(r)
- Constant declared in abstract class
- Constant declared in interface
- Empty class
- 'final' class(*)
- 'final' method(*)
- 'final' method in 'final' class(*)
- Inner class of interface(r)
- Limited-scope inner class(r)
- Marker interface
- Missing @Deprecated annotation(*)
- Missing @Override annotation(*)
- Multiple top level classes in single file(r)
- No-op method in abstract class
- Non-static initializer
- 'private' method declared 'final'(*)
- 'protected' member in 'final' class(*)
- 'public' constructor in non-'public' class(*)
- Singleton
- 'static' method declared 'final'(*)
- 'static', non-'final' field
- Utility class
- Utility class with public constructor(*)
- Utility class without private constructor(*)
- Cloning issues
- 'clone()' doesn't call 'super.clone()'
- 'clone()' doesn't declare CloneNotSupportedException?(*)
- 'clone()' instantiates objects with constructor
- 'clone()' method in non-Cloneable class(*)
- Cloneable class without 'clone()'
- Code maturity issues
- Call to 'printStackTrace()'
- Call to 'Thread.dumpStack()'
- Class without 'toString()'
- Inspection suppression annotation
- TODO comment
- Use of obsolete collection type
- Use of System.out or System.err
- Code style issues
- C-style array declaration(*)
- Chained equality comparisons
- Chained method calls(r)
- Class explicitly extends 'java.lang.Object'(*)
- Confusing octal escape sequence
- Constant on left side of comparison(*)
- Constant on right side of comparison(*)
- Control flow statement without braces(*)
- expression.equals("literal") rather than "literal".equals(expression)(*)
- Implicit call to 'super()'(*)
- Missorted modifers(*)
- Multiple variables in one declaration(*)
- Nested method call(r)
- Redundant field initialization(*)
- Redundant interface declaration(*)
- Redundant no-arg constructor(*)
- Return of 'this'
- Type parameter explicitly extends 'java.lang.Object'(*)
- Unnecessarily qualified static usage(*)
- Unnecessary call to 'super()'(*)
- Unnecessary code block(*)
- Unnecessary enum modifier(*)
- Unnecessary 'final' for local variable(*)
- Unnecessary 'final' for method parameter(*)
- Unnecessary fully qualified name(*)
- Unnecessary interface modifier(*)
- Unnecessary parentheses(*)
- Unnecessary qualifier for 'this'(*)
- Unnecessary semicolon(*)
- Unnecessary 'this' qualifier(*)
- Unqualified static usage(*)
- Variables of different types in one declaration(*)
- Control flow issues
- 'break' statement
- 'break' statement with label
- Conditional expression (?:)
- Conditional expression with identical branches(*)
- Conditional expression with negated condition(*)
- Conditional that can be simplified to && or ||(*)
- Confusing 'else' branch(*)
- Constant conditional expression(*)
- Constant if statement(*)
- 'continue' statement
- 'continue' statement with label
- 'default' not last case in 'switch'
- Duplicate condition in 'if' statement
- Enum 'switch' statement that misses case
- Fallthrough in 'switch' statement
- 'for' loop may be replaced by 'while' loop(*)
- 'for' loop with missing components
- 'if' statement with identical branches(*)
- 'if' statement with negated condition(*)
- 'if' statement with too many branches
- Infinite loop statement
- Labeled statement
- Local variable used and declared in different 'switch' branches
- Loop statement that doesn't loop
- Nested conditional expression
- Nested 'switch' statement
- Overly complex boolean expression(r)
- Pointless '.indexOf()' comparison
- Pointless boolean expression(*)
- Redundant conditional expression(*)
- Redundant 'if' statement(*)
- 'switch' statement
- 'switch' statement with too few branches
- 'switch' statement with too low of a branch density
- 'switch' statement with too many branches
- 'switch' statement without 'default' branch
- Unnecessary 'continue' statement(*)
- Unnecessary 'default' for enum switch statement
- Unnecessary label on 'break' statement(*)
- Unnecessary label on 'continue' statement(*)
- Unnecessary 'return' statement(*)
- Unused label(*)
- Data flow issues
- Redundant local variable(r)
- Reuse of local variable(*)
- Scope of variable is too broad(*)
- Encapsulation issues
- Accessing a non-public field of another object
- Assignment to Collection or array field from parameter
- Assignment to Date or Calendar field from parameter
- Package-visible field(r)
- Package-visible inner class(r)
- Protected field(r)
- Protected inner class(r)
- Public field(r)
- Public inner class(r)
- Return of Collection or array field
- Return of Date or Calendar field
- Error handling
- 'catch' generic class
- Checked exception class
- 'continue' or 'break' inside 'finally' block
- Empty 'catch' block
- Empty 'finally' block
- Empty 'try' block
- 'finally' block which can not complete normally
- 'instanceof' on 'catch' parameter
- java.lang.Error not rethrown
- java.lang.ThreadDeath not rethrown
- Nested 'try' statement
- Non-final field of exception class
- Overly broad 'catch' block
- Prohibited exception caught
- Prohibited exception declared
- Prohibited exception thrown
- 'return' inside 'finally' block
- 'throw' caught by containing 'try' statement
- 'throw' inside 'catch' block which ignores the caught exception
- 'throw' inside 'finally' block
- Unchecked exception class
- Unused 'catch' parameter
- Finalization issues
- 'finalize()' called explicitly
- 'finalize()' declaration
- 'finalize()' doesn't call 'super.finalize()'
- 'finalize()' not declared 'protected'(*)
- Imports
- * import
- Import from same package(*)
- java.lang import(*)
- Redundant import(*)
- Single class import
- Static import(*)
- Unused import(*)
- Inheritance issues
- Abstract class extends concrete class
- Abstract class which has no concrete subclass
- Abstract class without abstract methods
- Abstract method overrides abstract method(*)
- Abstract method overrides concrete method
- Abstract method with missing implementations
- Class explicitly extends a Collection class(*)
- Class extends annotation interface
- Constructor not 'protected' in 'abstract' class(*)
- Method is identical to its super method(*)
- Refused bequest
- Static inheritance(*)
- Initialization issues
- Abstract method call in constructor
- Instance variable may not be initialized(*)
- Instance variable used before initialized
- Non-final static variable is used during class initialization
- Overridable method call in constructor(*)
- Overridden method call in constructor
- Static variable may not be initialized(*)
- Static variable used before initialization
- 'this' reference escaped in object construction
- Unsafe lazy initialization of static field
- Internationalization issues
- Call to Date.toString()
- Call to Numeric .toString()
- Call to String.compareTo()
- Call to String.equals()
- Call to String.equalsIgnoreCase()
- Call to String.toUpperCase() or .toLowerCase() without a Locale
- Call to Time.toString()
- Character comparison
- Instantiating a SimpleDateFormat? without a Locale
- "Magic character"(r)
- String concatenation
- Use of StringTokenizer?
- J2ME issues
- Abstract class which has only one direct inheritor
- Anonymous inner class may be a named static inner class(*)
- Array.length in loop condition
- Connection opened but not safely closed
- Field repeatedly accessed in method
- Interface which has only one direct inheritor
- Large array allocation with no OutOfMemoryError? check
- Method call in loop condition
- Overly large initializer for array of primitive type
- Private member access between outer and inner classes(*)
- RecordStore? opened but not safely closed
- Single character '.startsWith()' or '.endsWith()'
- J2SDK5.0 specific issues and migration aids
- '.indexOf()' expression is replaceable by '.contains()'(*)
- 'for' loop replaceable by 'for each'(*)
- Raw use of parameterized class
- Unnecessary boxing(*)
- Unnecessary unboxing(*)
- 'while' loop replaceable by 'for each'(*)
- JUnit issues
- JUnit abstract test class naming convention(r)
- JUnit test case with no tests
- JUnit test class naming convention(r)
- JUnit test method without any assertions
- JUnit TestCase? in product source(r)
- JUnit TestCase? with non-trivial constructors
- Message missing on JUnit assertion
- Misordered 'assertEquals()' parameters(*)
- 'setUp()' doesn't call 'super.setUp()'(*)
- 'setup()' instead of 'setUp()'(r)
- 'setUp()' with incorrect signature
- Simplifiable JUnit assertion(*)
- 'suite()' method not declared 'static'
- 'tearDown()' doesn't call 'super.tearDown()'(*)
- 'teardown()' instead of 'tearDown()'(r)
- 'tearDown()' with incorrect signature
- Test method with incorrect signature
- Unconstructable JUnit TestCase?
- Java language level issues
- Annotation
- Annotation class
- 'assert' statement
- Auto-boxing(*)
- Auto-unboxing(*)
- Enumerated class
- Extended 'for' statement(*)
- Use of 'assert' as identifier(*)
- Use of 'enum' as identifier(*)
- Variable argument method
- JavaBeans issues
- Class without constructor(*)
- Class without no-arg constructor
- Field has setter but no getter
- Logging issues
- Class with multiple loggers
- Class without logger
- Non-constant logger
- Memory issues
- Calls to System.gc() or Runtime.gc()
- Static collection
- StringBuffer? field
- Zero-length array allocation(r)
- Method metrics
- Method with more than three negations
- Method with multiple loops
- Method with multiple return points.
- Method with too many exceptions declared
- Method with too many parameters
- Overly complex method
- Overly coupled method
- Overly long method
- Overly nested method
- Naming conventions
- Annotation naming convention(r)
- Boolean method name must start with question(r)
- Class name prefixed with package name(r)
- Class name same as ancestor name(r)
- Class naming convention(r)
- Confusing 'main()' method(r)
- Constant naming convention(r)
- Enumerated class naming convention(r)
- Enumerated constant naming convention(r)
- Exception class name doesn't end with Exception(r)
- Instance method naming convention(r)
- Instance variable naming convention(r)
- Interface naming convention(r)
- Local variable naming convention(r)
- Method name same as class name(r)
- Method name same as parent class name(r)
- Method names differing only by case(r)
- Method parameter naming convention(r)
- Non-boolean method name must not start with question word(r)
- Non-constant field with upper-case name(r)
- Non-exception class name ends with 'Exception'(r)
- Overloaded methods with same number of parameters
- Parameter name differs from parameter in overridden method(*)
- Questionable name(r)
- Standard variable names(r)
- Static method naming convention(r)
- Static variable naming convention(r)
- Type parameter naming convention(r)
- Use of '$' in identifier(r)
- Numeric issues
- '.equals()' called on BigDecimal?(*)
- Comparison of 'short' and 'char' values
- Comparison to NaN? or NaN?(*)
- Confusing floating-point literal(*)
- Constant call to java.lang.Math or StrictMath?(*)
- Division by zero
- Floating point equality comparison
- Implicit numeric conversion(*)
- Integer division in floating point context
- Long literal ending with 'l' instead of 'L'(*)
- Non-reproducible call to java.lang.Math(*)
- Number constructor call with primitive argument(*)
- Numeric cast that loses precision
- Octal integer
- Overly complex arithmetic expression(r)
- Pointless arithmetic expression(*)
- Performance issues
- Boolean constructor call(*)
- Call to simple getter from within class(*)
- Call to simple setter from within class(*)
- Collection without initial capacity
- Concatenation with empty string(*)
- Constant StringBuffer? may be String
- Field may be 'static'(*)
- Inner class may be 'static'(*)
- Instantiating object to get Class object(*)
- Manual array copy(*)
- Map replaceable by EnumMap?
- Method may be 'static'(*)
- Multiply or divide by power of two(*)
- Non-constant String should be StringBuffer?
- Object allocation in loop
- Redundant '.substring(0)'(*)
- Redundant String constructor call(*)
- Redundant 'String.toString()'(*)
- Set replaceable by EnumSet?
- Single character string concatenation(*)
- String concatenation in loop
- String concatenation inside StringBuffer?.append()(*)
- 'String.equals("")' instead of 'String.length()==0'(*)
- 'StringBuffer' may be 'StringBuilder' (J2SDK 5.0 only)(*)
- StringBuffer? or StringBuilder? without initial capacity
- StringBuffer?.toString() in concatenation(*)
- Tail recursion(*)
- Unnecessary temporary object in conversion from String(*)
- Unnecessary temporary object in conversion to String(*)
- Use of java.lang.reflect
- Using Random.nextDouble() to get random integer(*)
- Portability issues
- Call to 'Runtime.exec()'
- Call to 'System.exit()' or related methods
- Call to 'System.getenv()'
- Hardcoded file separator
- Hardcoded line separator
- Native method
- Use of AWT peer class
- Use of concrete JDBC driver class
- Use of java.lang.ProcessBuilder class
- Use of sun.* classes
- Probable bugs
- '.equals()' called on array type(*)
- Assignment to 'null'
- Assignment to static field from instance method
- Assignment used as condition(*)
- Call to default '.toString()'
- Cast conflicts with 'instanceof'
- Casting to incompatible interface
- Collection added to self
- 'compareto()' instead of 'compareTo()'(*)
- Confusing 'null' argument to var-arg method
- Covariant 'compareTo()'
- Covariant 'equals()'
- Empty class initializer(*)
- 'equal()' instead of 'equals()'(*)
- 'equals()' between objects of inconvertible types
- 'equals()' method which doesn't check class of parameter
- 'for' loop where update or condition doesn't use loop variable
- 'hashcode()' instead of 'hashCode()'(r)
- Infinite recursion
- 'instanceof' with incompatible interface
- Instantiation of utility class
- 'Iterator.hasNext()' which calls 'next()'
- 'Iterator.next()' which can't throw NoSuchElementException?
- Malformed format string
- Malformed regular expression
- Malformed XPath expression
- Mismatched query and update of collection
- Mismatched read and write of array
- Non-final field referenced in 'compareTo()'
- Non-final field referenced in 'equals()'
- Non-final field referenced in 'hashCode()'
- Non-short-circuit boolean expression(*)
- Object comparison using ==, instead of '.equals()'(*)
- Object.equals(null)
- Octal and decimal integers in same array
- Result of method call ignored
- Result of object allocation ignored
- Return of 'null'
- Statement with empty body
- Static field referenced via subclass(*)
- Static method referenced via subclass(*)
- String comparison using '==', instead of '.equals()'(*)
- Subtraction in compareTo()
- Suspicious 'Collections.toArray()' call
- Suspicious 'System.arraycopy()' call
- Text label in 'switch' statement
- 'tostring()' instead of 'toString()'(*)
- Use of archaic system property accessors
- Use of index 0 in JDBC ResultSet?
- Use of Properties object as a Hashtable
- Resource management issues
- Channel opened but not safely closed
- Hibernate resource opened but not safely closed
- I/O resource opened but not safely closed
- JDBC resource opened but not safely closed
- JNDI resource opened but not safely closed
- Socket opened but not safely closed
- Use of DriverManager? to get JDBC connection
- Security issues
- Access of system properties
- Call to 'Connection.prepareStatement()' or related method with non-constant string
- Call to 'Runtime.exec()' with non-constant string
- Call to 'Statement.execute()' or related method with non-constant string
- Call to 'System.loadLibrary()' with non-constant string
- Call to 'System.setSecurityManager()'
- ClassLoader? instantiation
- Cloneable class in secure context
- Custom ClassLoader?
- Custom SecurityManager?
- Deserializable class in secure context
- Non-final 'clone()' in secure context
- Non-static inner class in secure context
- Public static array field
- Public static collection field
- Serializable class in secure context
- Unsecure random number generation
- Serialization issues
- Externalizable class with 'readObject()' or 'writeObject()'
- Instance variable may not be initialized by 'readObject()'
- Non-serializable class with 'readObject()' or 'writeObject()'(*)
- Non-serializable class with 'serialVersionUID'
- 'readObject()' or 'writeObject()' not declared 'private'(*)
- 'readResolve()' or 'writeReplace()' not declared 'protected'(*)
- Serializable class with unconstructable ancestor
- Serializable class without 'readObject()' and 'writeObject()'
- Serializable class without serialVersionUID(*)
- Serializable non-static inner class without non-Serializable outer class
- Serializable non-static inner class without 'serialVersionUID'(*)
- 'serialPersistentFields' field not declared 'private static final ObjectStreamField?[]'
- 'serialVersionUID' field not declared 'private static final long'
- Transient field in non-serializable class(*)
- Threading issues
- Arithmetic operation on volatile field
- 'await()' not in loop
- Busy wait
- Call to a native method while locked
- Call to 'notify()' instead of 'notifyAll()'(*)
- Call to 'signal()' instead of 'signalAll()'(*)
- Call to 'System.runFinalizersOnExit()'
- Call to 'Thread.run()'(*)
- Call to 'Thread.setPriority()'
- Call to Thread.sleep() while synchronized
- Call to 'Thread.start()' during object construction
- Call to 'Thread.stop()', '.suspend()' or '.resume()'
- Call to 'Thread.yield()'
- Class explicitly extends java.lang.Thread(*)
- Double-checked locking
- Empty 'synchronized' statement
- Field accessed in both synchronized and unsynchronized contexts
- Instantiating a Thread with default 'run()' method
- Lock acquired but not safely unlocked
- Nested 'synchronized' statement
- Non-private field accessed in synchronized context
- Non-synchronized method overrides synchronized method
- 'notify()' or 'notifyAll()' called on Condition object
- 'notify()' or 'notifyAll()' while not synced
- 'notify()' or 'notifyAll()' without corresponding state change
- Synchronization on a java.util.concurrent.locks.Lock object
- Synchronization on a non-final field
- Synchronization on 'this'
- 'synchronized' method(*)
- Unconditional 'wait()' call
- Volatile array field
- Volatile long or double field
- 'wait()' called on Condition object
- 'wait()' not in loop
- 'wait()' while holding two locks
- 'wait()' while not synced
- While loop spins on field
- Visibility issues
- Class escapes defined scope
- Field name hides field in superclass(r)
- Inner class field hides outer class field(r)
- Local variable hides member variable(r)
- Method overloads method of superclass(r)
- Method overrides package local method of superclass located in other package(r)
- Method overrides private method of superclass(r)
- Method overrides static method of superclass(r)
- Parameter hides member variable(r)
- Type parameter hides visible type(r)
There are two different ways to use InspectionGadgets. For the first, the inspections provided show up in the inspections panel just like the stock inspections provided by IDEA. From there, you can select inspections to be run, configure the inspections if necesssary, and create named inspection set configurations for ease of use later. Additionally, you can select inspections to be run continously during editing, which will result in "yellow-line" warnings in the open edit window. This is done through the IDEA Settings/Errors panel.
Q: Hey! I use [insert name of favorite construct] all the time, and I've never had a problem with it. Are you saying I'm a bad developer! Where do you get off having an inspection for it!
A: If you don't feel a given inspection is appropriate for your needs, than by all means don't use it. The goal of InspectionGadgets is to use automated static analysis to lead to better code, easier team interactions, and more productive code reviews. If one or more of the inspections doesn't aid this goal in your environment, well that's why the inspections are independently toggleable. I'm not interested in religious wars, here, particularly about inspections that I personally don't always use myself.
Q: How is InspectionGadgets any different than JTest (or PMD, or FindBugs?, or Code Coach or, TeamStudio? or, or...)?
A: While there are other commercial and open-source static analysis tools available, there are several features that make InspectionGadgets a far more useful tool than others your might have used. The most important is the level of integration. Any of the inspections provided can be configured to be continually detected as you are editing a file, and presented as a "yellow-line" warning. This provides immediate feedback on these coding issues as you are editing, rather than after the run of a separate product or test case. Second, many of the inspections provided by InspectionGadgets come with automatic "QuickFixes", which can be triggered either from the inspection results tool window, or from the editor (as intentions). Combined with the "Goto next error" (F2) editor command, this can make it very quick and easy to find and fix any issues you found. Finally, I gleaned the list of inspections that I've included with InspectionGadgets from the lists of inspections provided by those other products, with the intention of including everything that is in those products that was worth doing. It is hoped that you'll find InspectionGadgets covers more of your static analysis needs than any other product. If not, I'll probably extend it. If I don't cover more issues now than any other static analysis tool, I will before I'm done.
Q: I've got this idea for an inspection that would help me find a lot of bugs, and I'm betting it would help a lot of other people as well. How do I go about getting it added?
A: Drop me a line, with either a clear explanation of what your inspection would do or the code to do it. I'm always on the lookout for new static analyses. The only exceptions are things which would require global or package-level analyses (which the Inspection API doesn't allow) or inspections which can already be automatically fixed by other, non-inspection tools in IDEA (e.g., code layout).
Q: A lot of other static analysis tools provide little languages for constructing your own inspections. Some even have little visual languages, where you can drag-and-drop component tests to build inspections. Are there any plans for adding something like that in InspectionGadgets?.
A: No. There's already a perfectly good language for adding inspections to InspectionGadgets. It's called Java, and you're undoubtedly familiar with it. If you've got a custom inspection you would like to add which wouldn't be useful to the general community, the InspectionGadgets source provides 514 examples of how it is done. It's not rocket surgery.
Q: We have a bunch of developers here. I'm guessing that they're all not going to want to plow through the Errors panel, setting up each and every one of our preferred error or warning levels. Are these settings stored to a file that I could configure once and distribute to the other developers?
A: Use the Export Settings... and Import Settings... actions provided in IDEA's File menu. There you'll want to export 'Error highlighting settings, CodeInsight? settings'. The resulting file can be distributed to other developers or included in your project source repository for easy access.
Alternatively in Linux, the File-Settings-Errors configuration is stored in:
~/.IntelliJIdea/config/options/editor.codeinsight.xml
In Windows, it is stored in:
C:\Documents and Settings\<userId>\.IntelliJIdea\config\options\editor.codeinsight.xml
InspectionGadgets is distributed with IntelliJ IDEA 4.5 and higher and installed by default.
While all errors in InspectionGadgets are mine, this plugin wouldn’t have been possible without input and assistance from a lot of people. Thanks go to Richard Gillam, Bryon Jacob, John DeRegnaucourt, and Mark Reed for review of my proposed inspections, to the PsiViewer? team for making this plugin about fifty times easier to code and debug, to Maxim Shafirov doing the work to open up the Inspection API and then not complaining too much when I needed fixes to it, and my wife Brenda for putting up with me while I invested night after night in this thing.
-- DaveGriffith - 28 Oct 2003
Since August 2, 2005 I am official maintainer of this plugin and before that time I've made many contributions. Thanks Dave, for such an excellent plugin.
-- BasLeijdekkers - 16 Nov 2005
Related Topics: PluginDocumentation, ProjectPluginTemplate, OpenAPI, IntellijPluginDocumentation,
IntellijPluginDocumentation, PluginDeployment, IdeasForPlugins
|
|