src/checkstyle/checks/Check.hx
package checkstyle.checks;
import checkstyle.config.ExcludeRange;
import haxe.Timer;
class Check {
public var severity:SeverityLevel;
public var type(default, null):CheckType;
public var categories:Array<Category>;
public var points:Int;
public var desc:String;
var messages:Array<Message>;
var moduleName:String;
var checker:Checker;
public function new(type:CheckType) {
this.type = type;
severity = SeverityLevel.INFO;
categories = [Category.STYLE];
points = 1;
desc = haxe.rtti.Meta.getType(Type.getClass(this)).desc[0];
}
public function reset() {
messages = [];
}
public function configureProperty(name:String, value:Any) {
Reflect.setField(this, name, value);
}
public function run(checker:Checker):Array<Message> {
var startTime = Timer.stamp();
if (checker.verbose) {
Sys.println('${checker.file.name} - [${getModuleName()}] start');
}
reset();
this.checker = checker;
if (severity != SeverityLevel.IGNORE) {
try {
actualRun();
}
catch (e:Exception) {
ErrorUtils.handleException(e, checker.file, getModuleName());
}
}
var endTime = Timer.stamp();
if (checker.verbose) {
Sys.println('${checker.file.name} - [${getModuleName()}] done. (${(endTime - startTime) * 1000}ms)');
}
return messages;
}
function actualRun() {
throw new Exception("Unimplemented");
}
public function logPos(msg:String, pos:Position, ?code:String, ?sev:SeverityLevel):Message {
return logRange(msg, pos.min, pos.max, code, sev);
}
public function logRange(msg:String, startPos:Int, endPos:Int, ?code:String, ?sev:SeverityLevel):Message {
var message:Message = createRangeMessage(msg, startPos, endPos, code, sev);
messages.push(message);
return message;
}
function offsetToColumn(lp:LinePos):Int {
if (checker.lines.length <= lp.line) return lp.ofs;
var line:Bytes = Bytes.ofString(checker.lines[lp.line]);
return line.getString(0, lp.ofs).length;
}
public function log(msg:String, startLine:Int, startColumn:Int, endLine:Int, endColumn:Int, ?code:String, ?sev:SeverityLevel):Message {
var message:Message = createMessage(msg, startLine, startColumn, endLine, endColumn, code, sev);
messages.push(message);
return message;
}
public function createRangeMessage(msg:String, startPos:Int, endPos:Int, ?code:String, ?sev:SeverityLevel):Message {
var lpStart = checker.getLinePos(startPos);
var lpEnd = checker.getLinePos(endPos);
var startColumn:Int = offsetToColumn(lpStart);
var endColumn:Int = offsetToColumn(lpEnd);
return createMessage(msg, lpStart.line + 1, startColumn, lpEnd.line + 1, endColumn, code, sev);
}
function createMessage(msg:String, startLine:Int, startColumn:Int, endLine:Int, endColumn:Int, ?code:String, ?sev:SeverityLevel):Message {
if (sev == null) sev = severity;
return {
fileName: checker.file.name,
message: msg,
code: code,
desc: desc,
severity: sev,
moduleName: getModuleName(),
categories: categories,
points: points,
range: {
start: {
line: startLine,
column: startColumn
},
end: {
line: endLine,
column: endColumn
}
},
related: []
}
}
public function addRelatedRange(message:Message, msg:String, startPos:Int, endPos:Int) {
var lpStart = checker.getLinePos(startPos);
var lpEnd = checker.getLinePos(endPos);
var startColumn:Int = offsetToColumn(lpStart);
var endColumn:Int = offsetToColumn(lpEnd);
addRelatedMessage(message, lpStart.line + 1, startColumn, lpEnd.line + 1, endColumn);
}
public function addRelatedMessage(message:Message, startLine:Int, startColumn:Int, endLine:Int, endColumn:Int) {
message.related.push({
fileName: checker.file.name,
range: {
start: {
line: startLine,
column: startColumn
},
end: {
line: endLine,
column: endColumn
}
}
});
}
public function getModuleName():String {
if (moduleName == null) moduleName = ChecksInfo.getCheckName(this);
return moduleName;
}
function forEachField(cb:Field -> ParentType -> Void) {
if (checker.ast == null) return;
if (checker.ast.decls == null) return;
for (td in checker.ast.decls) {
var fields:Array<Field> = switch (td.decl) {
case EClass(d): d.data;
case EAbstract(a): a.data;
default: null;
}
if (fields == null) continue;
for (field in fields) {
if (!isCheckSuppressed(field)) cb(field, td.decl.toParentType());
}
}
}
function isCheckSuppressed(f:Field):Bool {
if (f == null) return false;
return isPosSuppressed(f.pos);
}
function isLineSuppressed(i:Int):Bool {
if (checker.linesIdx.length <= i) return false;
return isCharPosSuppressed(checker.linesIdx[i].l);
}
function isPosExtern(pos:Position):Bool {
return isCharPosExtern(pos.min);
}
function isPosSuppressed(pos:Position):Bool {
return isCharPosSuppressed(pos.min);
}
function isCharPosSuppressed(pos:Int):Bool {
var ranges:Array<ExcludeRange> = checker.excludesRanges.get(getModuleName());
if (ranges == null) return false;
if (ranges.length <= 0) return false;
for (range in ranges) {
if ((range.charPosStart <= pos) && (range.charPosEnd >= pos)) return true;
}
return false;
}
function isCharPosExtern(pos:Int):Bool {
if (checker.ast == null) return false;
if (checker.ast.decls == null) return false;
for (td in checker.ast.decls) {
switch (td.decl) {
case EAbstract(d):
case EClass(d):
if ((pos <= td.pos.max) && (pos >= td.pos.min)) return d.flags.contains(HExtern);
case EEnum(d):
if ((pos <= td.pos.max) && (pos >= td.pos.min)) return d.flags.contains(EExtern);
case ETypedef(d):
if ((pos <= td.pos.max) && (pos >= td.pos.min)) return d.flags.contains(TDExtern);
switch (d.data) {
case TAnonymous(fields):
for (field in fields) {
if (pos > field.pos.max) continue;
if (pos < field.pos.min) continue;
return d.flags.contains(TDExtern);
}
default:
}
default:
}
}
return false;
}
function checkSuppressionConst(e:Expr, search:String):Bool {
switch (e.expr) {
case EArrayDecl(a):
for (e1 in a) {
if (checkSuppressionConst(e1, search)) return true;
}
case EConst(c):
switch (c) {
case CString(s):
if (s == search) return true;
default:
}
default:
}
return false;
}
public function detectableInstances():DetectableInstances {
return [];
}
}
enum CheckType {
AST;
TOKEN;
LINE;
}