All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cflint.CFLint Maven / Gradle / Ivy

Go to download

A static code analysis tool for ColdFusion (in the spirit of FindBugs and Lint). With CFLint, you are able to analyze your ColdFusion code base for code violations.

There is a newer version: 1.5.0
Show newest version
package com.cflint;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.antlr.runtime.BitSet;
import org.antlr.v4.runtime.ANTLRErrorListener;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.atn.ATNConfigSet;
import org.antlr.v4.runtime.dfa.DFA;

import com.cflint.BugInfo.BugInfoBuilder;
import com.cflint.config.CFLintChainedConfig;
import com.cflint.config.CFLintConfig;
import com.cflint.config.CFLintConfiguration;
import com.cflint.config.CFLintPluginInfo;
import com.cflint.config.CFLintPluginInfo.PluginInfoRule;
import com.cflint.config.CFLintPluginInfo.PluginInfoRule.PluginMessage;
import com.cflint.config.ConfigUtils;
import com.cflint.exception.CFLintScanException;
import com.cflint.listeners.ScanProgressListener;
import com.cflint.plugins.CFLintScanner;
import com.cflint.plugins.CFLintSet;
import com.cflint.plugins.CFLintStructureListener;
import com.cflint.plugins.Context;
import com.cflint.plugins.Context.ContextMessage;
import com.cflint.plugins.Context.ContextType;
import com.cflint.plugins.core.CFScopes;
import com.cflint.plugins.exceptions.CFLintExceptionListener;
import com.cflint.plugins.exceptions.DefaultCFLintExceptionListener;
import com.cflint.tools.AllowedExtensionsLoader;
import com.cflint.tools.CFLintFilter;
import com.cflint.tools.CFMLTagInfo;
import com.cflint.tools.CommentReformatting;
import com.cflint.tools.FileUtil;
import com.cflint.tools.PrecedingCommentReader;
import com.cflint.tools.ScanningProgressMonitorLookAhead;

import cfml.CFSCRIPTLexer;
import cfml.CFSCRIPTParser;
import cfml.parsing.CFMLParser;
import cfml.parsing.CFMLSource;
import cfml.parsing.ParserTag;
import cfml.parsing.cfscript.CFArrayExpression;
import cfml.parsing.cfscript.CFAssignmentExpression;
import cfml.parsing.cfscript.CFExpression;
import cfml.parsing.cfscript.CFFullVarExpression;
import cfml.parsing.cfscript.CFFunctionExpression;
import cfml.parsing.cfscript.CFIdentifier;
import cfml.parsing.cfscript.CFLiteral;
import cfml.parsing.cfscript.CFMember;
import cfml.parsing.cfscript.CFNewExpression;
import cfml.parsing.cfscript.CFStatement;
import cfml.parsing.cfscript.CFStringExpression;
import cfml.parsing.cfscript.CFStructElementExpression;
import cfml.parsing.cfscript.CFTernaryExpression;
import cfml.parsing.cfscript.CFVarDeclExpression;
import cfml.parsing.cfscript.script.CFCase;
import cfml.parsing.cfscript.script.CFCatchStatement;
import cfml.parsing.cfscript.script.CFCompDeclStatement;
import cfml.parsing.cfscript.script.CFCompoundStatement;
import cfml.parsing.cfscript.script.CFExpressionStatement;
import cfml.parsing.cfscript.script.CFForInStatement;
import cfml.parsing.cfscript.script.CFForStatement;
import cfml.parsing.cfscript.script.CFFuncDeclStatement;
import cfml.parsing.cfscript.script.CFFunctionParameter;
import cfml.parsing.cfscript.script.CFIfStatement;
import cfml.parsing.cfscript.script.CFIncludeStatement;
import cfml.parsing.cfscript.script.CFPropertyStatement;
import cfml.parsing.cfscript.script.CFReturnStatement;
import cfml.parsing.cfscript.script.CFScriptStatement;
import cfml.parsing.cfscript.script.CFSwitchStatement;
import cfml.parsing.cfscript.script.CFTryCatchStatement;
import cfml.parsing.cfscript.script.CFWhileStatement;
import cfml.parsing.reporting.ArrayErrorListener;
import cfml.parsing.reporting.IErrorReporter;
import cfml.parsing.reporting.ParseException;
import net.htmlparser.jericho.Attribute;
import net.htmlparser.jericho.Element;
import net.htmlparser.jericho.EndTag;
import net.htmlparser.jericho.Source;

public class CFLint implements IErrorReporter {

    // constants
    private static final String FILE_ERROR = "FILE_ERROR";
    private static final String PARSE_ERROR = "PARSE_ERROR";
    private static final String PLUGIN_ERROR = "PLUGIN_ERROR";
    private static final String MISSING_SEMI = "MISSING_SEMI";
    private static final String AVOID_EMPTY_FILES = "AVOID_EMPTY_FILES";
    private static final String RESOURCE_BUNDLE_NAME = "com.cflint.cflint";

    private CFMLTagInfo tagInfo;
    private CFMLParser cfmlParser = new CFMLParser();
    private StackHandler handler = new StackHandler();
    private BugList bugs;
    private final List extensions = new ArrayList<>();
    private final List allowedExtensions = new ArrayList<>();
    private boolean verbose = false;
    private boolean logError = false;
    private boolean quiet = false;
    private boolean debug = false;
    private boolean showProgress = false;
    private boolean progressUsesThread = true;
    private CFLintStats stats = new CFLintStats();
    private final List scanProgressListeners = new ArrayList<>();
    private final List exceptionListeners = new ArrayList<>();
    private CFLintConfiguration configuration;
    private int skipToPosition = 0;
    private String currentFile = null;
    private Element currentElement = null;
    private boolean strictInclude;
    private Set> processed = new HashSet<>();

    // Stack to store include file depth to ensure no recursion
    private final Stack includeFileStack = new Stack<>();
    private int[] lineOffsets;

    public CFLint(final CFLintConfiguration configFile) throws IOException {
        final CFLintFilter filter = CFLintFilter.createFilter(verbose);
        bugs = new BugList(filter);
        cfmlParser.setErrorReporter(this);
        tagInfo = new CFMLTagInfo(cfmlParser.getDictionary());
        setConfiguration(configFile);
        if (exceptionListeners.isEmpty()) {
            addExceptionListener(new DefaultCFLintExceptionListener(bugs));
        }
    }

    public void setConfiguration(final CFLintConfiguration configFile) throws IOException {
        configuration = configFile == null ? new CFLintConfig() : configFile;
        
        extensions.clear();
        allowedExtensions.clear();
        scanProgressListeners.clear();
        exceptionListeners.clear();
        processed.clear();
        
        for (final PluginInfoRule ruleInfo : configuration.getRules()) {
            addScanner(ConfigUtils.loadPlugin(ruleInfo));// TODO load them all
        }
        allowedExtensions.addAll(AllowedExtensionsLoader.init(RESOURCE_BUNDLE_NAME));
        bugs.clearBugList();
    }

    @Deprecated
    public CFLint(final CFLintConfiguration configuration, final CFLintScanner... bugsScanners) {
        super();
        this.configuration = configuration;

        for (final CFLintScanner scanner : bugsScanners) {
            extensions.add(scanner);
            if (configuration != null) {
                final PluginInfoRule ruleInfo = configuration.getRuleByClass(scanner.getClass());
                if (ruleInfo != null) {
                    ruleInfo.setPluginInstance(scanner);
                }
            }
            if (scanner instanceof CFLintSet) {
                ((CFLintSet) scanner).setCFLint(this);
            }
        }
        CFLintFilter filter;
        try {
            filter = CFLintFilter.createFilter(verbose);
            bugs = new BugList(filter);
        } catch (final IOException e1) {
            e1.printStackTrace();
        }
        if (exceptionListeners.isEmpty()) {
            addExceptionListener(new DefaultCFLintExceptionListener(bugs));
        }
        allowedExtensions.addAll(AllowedExtensionsLoader.init(RESOURCE_BUNDLE_NAME));
        cfmlParser.setErrorReporter(this);
        tagInfo = new CFMLTagInfo(cfmlParser.getDictionary());
    }

    public CFLintStats getStats() {
        return stats;
    }

    public void scan(final String folderName) {
        if (showProgress) {
            ScanningProgressMonitorLookAhead.createInstance(this, folderName, progressUsesThread).startPreScan();
        }
        final File starterFile = new File(folderName);
        setupConfigAncestry(starterFile.getAbsoluteFile().getParentFile());
        scan(starterFile);
        fireClose();
    }

    public void setupConfigAncestry(File folder) {
        final Stack configFiles = new Stack<>();
        fileLoop: while (folder != null && folder.exists()) {
            for (final File file : folder.listFiles()) {
                if (file.getName().toLowerCase().startsWith(".cflintrc")) {
                    if (verbose) {
                        System.out.println("read config " + file);
                    }
                    try {
                        @SuppressWarnings("deprecation")
                        final CFLintConfig newConfig = file.getName().toLowerCase().endsWith(".xml")
                                ? ConfigUtils.unmarshal(file, CFLintConfig.class)
                                : ConfigUtils.unmarshalJson(new FileInputStream(file), CFLintConfig.class);
                        configFiles.push(newConfig);
                        if (!newConfig.isInheritParent()) {
                            break fileLoop;
                        }
                    } catch (final Exception e) {
                        if (!quiet) {
                            System.err.println("Could not read config file " + file + ". Check for duplicates.");
                        }
                    }
                }
            }
            folder = folder.getParentFile();
        }
        for (final CFLintConfig newConfig : configFiles) {
            configuration = new CFLintChainedConfig(newConfig, configuration);
        }
    }

    public void scan(final File folderOrFile) {

        if (debug) {
            System.out.println("Current file: " + folderOrFile.getAbsolutePath());
        }

        if (getBugs().getFileFilter() != null && !getBugs().getFileFilter().includeFile(folderOrFile)) {
            return;
        }
        if (!folderOrFile.exists()) {
            System.err.println("File " + folderOrFile + " does not exist.");
            return;
        }
        if (folderOrFile.isDirectory()) {
            if (verbose) {
                if (folderOrFile.getName().startsWith("."))
                {
                    System.out.println("Skipping folder and its children: " + folderOrFile.getAbsolutePath());
                    return;
                }
            }
            final CFLintConfiguration saveConfig = configuration;
            try {
                for (final File file : folderOrFile.listFiles()) {
                    if (file.getName().toLowerCase().startsWith(".cflintrc")) {
                        try {
                            if (verbose) {
                                System.out.println("read config " + file);
                            }
                            @SuppressWarnings("deprecation")
                            final CFLintConfiguration newConfig = file.getName().toLowerCase().endsWith(".xml")
                                    ? ConfigUtils.unmarshal(file, CFLintConfig.class)
                                    : ConfigUtils.unmarshalJson(new FileInputStream(file), CFLintConfig.class);
                            configuration = new CFLintChainedConfig(newConfig, configuration);
                        } catch (final Exception e) {
                            System.err.println("Could not read config file " + file);
                        }
                    }
                }
                for (final File file : folderOrFile.listFiles()) {
                    scan(file);
                }
            } finally {
                configuration = saveConfig;
            }
        } else if (!folderOrFile.isHidden() && FileUtil.checkExtension(folderOrFile, allowedExtensions)) {
            if (!debug && verbose) {
                System.out.println("Current file: " + folderOrFile.getAbsolutePath());
            }
            final String src = FileUtil.loadFile(folderOrFile);
            includeFileStack.clear();
            try {
                // Report number of lines in the source
                stats.addFile(src == null || src.length() == 0 ? 0 : src.split("\\R").length + 1);
                process(src, folderOrFile.getAbsolutePath());
            } catch (final Exception e) {
                printException(e);
                if (logError) {
                    System.err.println("Logging Error: " + FILE_ERROR);
                    fireCFLintException(e, FILE_ERROR, folderOrFile.getAbsolutePath(), null, null, null, null);
                }
            }
        }
    }

    protected void printException(final Exception e, final Element... elem) {
        e.printStackTrace();
        if (!quiet) {
            if (elem != null && elem.length > 0 && elem[0] != null) {
                final int line = elem[0].getSource().getRow(elem[0].getBegin());
                System.err.println("Error in: " + shortSource(elem[0].getSource(), line) + " @ " + line + ":");
            }
            if (verbose) {
                e.printStackTrace(System.err);
            } else {
                System.err.println("Error: \"" + e.getMessage() + "\" Location: " + (e.getStackTrace().length > 0 ? e.getStackTrace()[0] : "''"));
            }
        }
    }

    public void process(final String src, final String filename) throws CFLintScanException {
        try{
            fireStartedProcessing(filename);
            lineOffsets = null;
            if (src == null || src.trim().length() == 0) {
                final Context context = new Context(filename, null, null, false, handler,configuration);
                reportRule(null, null, context, null, new ContextMessage(AVOID_EMPTY_FILES, null));
            } else {
                lineOffsets = getLineOffsets(src.split("\n"));
                final CFMLSource cfmlSource = new CFMLSource(src.contains(" in the tag hierarchy
     * 
     * @param element
     *            The element to process
     * @param msgcode
     *            The message code to check for
     * @return true if the msgcode is disabled for the given element.
     */
    protected boolean checkForDisabled(final Element element, final String msgcode) {
        Element elem = element;
        while (elem != null) {
            final Element prevSibling = getPreviousSibling(elem);
            if (prevSibling != null && prevSibling.getName().equals(CF.COMMENT)) {
                final Pattern p = Pattern.compile(".*---\\s*CFLINT-DISABLE\\s+(.*)\\s*---.*");
                final Matcher m = p.matcher(prevSibling.toString().toUpperCase().trim());
                if (m.matches()) {
                    // No message codes in CFLINT-DISABLE
                    if (m.group(1).trim().length() == 0) {
                        if (verbose) {
                            System.out.println("Skipping disabled " + msgcode);
                        }
                        return true;
                    }
                    // check for matching message codes in CFLINT-DISABLE
                    for (String skipcode : m.group(1).split(",")) {
                        skipcode = skipcode.trim();
                        if (msgcode.equals(skipcode)) {
                            if (verbose) {
                                System.out.println("Skipping disabled " + msgcode);
                            }
                            return true;
                        }
                    }
                }
            }
            elem = elem.getParentElement();
        }
        return false;
    }

    public void reportRule(final Element elem, final Object currentExpression, final Context context,
            final CFLintScanner pluginParm, final ContextMessage msg) {
        final Object expression = msg.getCfExpression() != null? msg.getCfExpression():currentExpression;
        // If we are processing includes, do NOT report any errors
        if (!includeFileStack.isEmpty()) {
            return;
        }
        final String msgcode = msg.getMessageCode();
        final String nameVar = msg.getVariable();
        final CFLintScanner plugin = msg.getSource() == null ? pluginParm : msg.getSource();
        if (checkForDisabled(elem, msgcode)) {
            return;
        }
        if (configuration == null) {
            throw new NullPointerException("Configuration is null");
        }
        PluginInfoRule ruleInfo;
        if (MISSING_SEMI.equals(msgcode)) {
            ruleInfo = new PluginInfoRule();
            final PluginMessage msgInfo = new PluginMessage(MISSING_SEMI);
            msgInfo.setMessageText("End of statement(;) expected after ${variable}");
            msgInfo.setSeverity(Levels.ERROR);
            ruleInfo.getMessages().add(msgInfo);
        } else if (PLUGIN_ERROR.equals(msgcode)) {
            ruleInfo = new PluginInfoRule();
            final PluginMessage msgInfo = new PluginMessage(PLUGIN_ERROR);
            msgInfo.setMessageText("Error in plugin: ${variable}");
            msgInfo.setSeverity(Levels.ERROR);
            ruleInfo.getMessages().add(msgInfo);
        } else if (AVOID_EMPTY_FILES.equals(msgcode)) {
            ruleInfo = new PluginInfoRule();
            final PluginMessage msgInfo = new PluginMessage(AVOID_EMPTY_FILES);
            msgInfo.setMessageText("CF file is empty: ${file}");
            msgInfo.setSeverity(Levels.WARNING);
            ruleInfo.getMessages().add(msgInfo);
        } else if (PARSE_ERROR.equals(msgcode)) {
            ruleInfo = new CFLintPluginInfo.PluginInfoRule();
            final CFLintPluginInfo.PluginInfoRule.PluginMessage msgInfo = new CFLintPluginInfo.PluginInfoRule.PluginMessage(
                    PARSE_ERROR);
            msgInfo.setMessageText("Unable to parse");
            msgInfo.setSeverity(Levels.ERROR);
            ruleInfo.getMessages().add(msgInfo);
        } else {
            if (plugin == null) {
                throw new NullPointerException(
                        "Plugin not set.  Plugin should be using addMessage(messageCode,variable,source) to report messages in parent contexts");
            }
            ruleInfo = configuration.getRuleForPlugin(plugin);

        }
        if (ruleInfo == null) {
            throw new NullPointerException("Rule not found for " + plugin.getClass().getSimpleName());
        }
        final PluginMessage msgInfo = ruleInfo.getMessageByCode(msgcode);
        if (msgInfo == null) {
            throw new NullPointerException(
                    "Message definition not found for [" + msgcode + "] in " + plugin.getClass().getSimpleName());
        }
        final BugInfoBuilder bldr = new BugInfo.BugInfoBuilder().setMessageCode(msgcode).setVariable(nameVar)
                .setFunction(context.getFunctionName()).setFilename(context.getFilename())
                .setComponent(context.getComponentName());

        bldr.setSeverity(msgInfo.getSeverity());
        bldr.setMessage(msgInfo.getMessageText());

        if (expression instanceof CFStatement) {
            bldr.setExpression(((CFStatement) expression).Decompile(0));
        } else if (expression instanceof CFScriptStatement) {
            bldr.setExpression(((CFScriptStatement) expression).Decompile(0));
        } else if (elem != null) {
            bldr.setExpression(elem.toString().replaceAll("\r\n", "\n"));
        }
        bldr.setRuleParameters(ruleInfo.getParameters());
        if (configuration.includes(ruleInfo.getMessageByCode(msgcode))
                && !configuration.excludes(ruleInfo.getMessageByCode(msgcode))) {
            //A bit of a hack to fix the offset issue 
            //This could be handled better at the source where line and offset are calc'd.
            int idxOffSet = 1;
            try{
                if(lineOffsets != null && msg.getLine()!=null && msg.getOffset() != null && msg.getOffset() >=lineOffsets[msg.getLine()] ){
                    idxOffSet=0;
                }
            }catch(ArrayIndexOutOfBoundsException e){}
            if (expression instanceof CFExpression) {
                final BugInfo bugInfo = bldr.build((CFExpression) expression, elem);
                final Token token = ((CFExpression) expression).getToken();
                if (!suppressed(bugInfo, token, context)) {
                    bugs.add(bugInfo);
                }
                //If the context expression is attached, use the context line and column
                if( msg.getCfExpression() != null && msg.getCfExpression() != currentExpression){
                    if (msg.getLine() != null) {
                        bugInfo.setLine(msg.getLine());
                        if (msg.getOffset() != null) {
                            bugInfo.setOffset(msg.getOffset());
                            bugInfo.setColumn(msg.getOffset() - lineOffsets[msg.getLine() - idxOffSet]);
                        } else {
                            bugInfo.setOffset(lineOffsets != null ? lineOffsets[msg.getLine() - idxOffSet] : 0);
                            bugInfo.setColumn(0);
                        }
                    }    
                }
            } else {
                final BugInfo bug = bldr.build((CFScriptStatement) expression, elem);
                if (msg.getLine() != null) {
                    bug.setLine(msg.getLine());
                    if (msg.getOffset() != null) {
                        bug.setOffset(msg.getOffset());
                        bug.setColumn(msg.getOffset() - lineOffsets[msg.getLine() - idxOffSet]);
                    } else {
                        bug.setOffset(lineOffsets != null ? lineOffsets[msg.getLine() - idxOffSet] : 0);
                        bug.setColumn(0);
                    }
                    bug.setLength(msg.getVariable() != null ? msg.getVariable().length() : 0);
                }
                if (!suppressed(bug, expression==null?null:((CFScriptStatement)expression).getToken(), context)) {
                    bugs.add(bug);
                }
            }
        }
    }

    /*
     * Look for a suppress comment on the same line. cflint:line - suppresses
     * any messages on the same line cflint:MESSAGE_CODE - suppresses any
     * message matching that code
     */
    protected boolean suppressed(final BugInfo bugInfo, final Token token, final Context context) {
        if (context == null || context.isSuppressed(bugInfo)) {
            return true;
        }
        if(token == null){
            return false;
        }
        final Iterable tokens = context.afterTokens(token);
        for (final Token currentTok : tokens) {
            if (debug) {
                System.out.println(currentTok.toString());
            }
            if (currentTok.getLine() != token.getLine()) {
                break;
            }
            if (currentTok.getChannel() == Token.HIDDEN_CHANNEL && currentTok.getType() == CFSCRIPTLexer.LINE_COMMENT) {
                final String commentText = currentTok.getText().replaceFirst("^//\\s*", "").trim();
                if (commentText.startsWith("cflint ")) {
                    final Pattern pattern = Pattern.compile("cflint\\s+ignore:([\\w,]+).*");
                    final Matcher matcher = pattern.matcher(commentText);
                    if (matcher.matches() && matcher.groupCount() > 0) {
                        final String ignoreCodes = matcher.group(1);
                        if ("line".equalsIgnoreCase(ignoreCodes)) {
                            return true;
                        }
                        for (final String ignoreCode : ignoreCodes.split(",\\s*")) {
                            if (ignoreCode.equals(bugInfo.getMessageCode())) {
                                return true;
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    public BugList getBugs() {
        return bugs;
    }

    public List getAllowedExtensions() {
        return allowedExtensions;
    }

    public void setAllowedExtensions(final List allowedExtensions) {
        this.allowedExtensions.clear();
        if(allowedExtensions != null){
            this.allowedExtensions.addAll(allowedExtensions);
        }
    }

    @Override
    public void reportError(final String arg0) {
        // Empty implementation
    }

    public void setVerbose(final boolean verbose) {
        this.verbose = verbose;
    }

    public void setLogError(final boolean logError) {
        this.logError = logError;
    }

    public void setQuiet(final boolean quiet) {
        this.quiet = quiet;
    }

    public void setDebug(final boolean debug) {
        this.debug = debug;
    }

    public void addScanProgressListener(final ScanProgressListener scanProgressListener) {
        scanProgressListeners.add(scanProgressListener);
    }

    protected void fireStartedProcessing(final String srcidentifier) {
        cfmlParser = new CFMLParser();
        cfmlParser.setErrorReporter(this);
        currentFile = srcidentifier;
        currentElement = null;
        for (final CFLintStructureListener structurePlugin : getStructureListeners(extensions)) {
            try {
                structurePlugin.startFile(srcidentifier, bugs);
            } catch (final Exception e) {
                printException(e);
                final Context context = new Context(currentFile, currentElement, null, true, null, null);
                final ContextMessage cm = new ContextMessage(PARSE_ERROR, null, null, context.startLine());
                reportRule(currentElement, null, context, null, cm);
            }
        }
        for (final ScanProgressListener p : scanProgressListeners) {
            p.startedProcessing(srcidentifier);
        }
    }

    protected void fireFinishedProcessing(final String srcidentifier) {
        for (final CFLintStructureListener structurePlugin : getStructureListeners(extensions)) {
            try {
                structurePlugin.endFile(srcidentifier, bugs);
            } catch (final Exception e) {
                printException(e);
                final Context context = new Context(currentFile, currentElement, null, true, null, null);
                final ContextMessage cm = new ContextMessage(PARSE_ERROR, null, null, context.startLine());
                reportRule(currentElement, null, context, null, cm);
            }
        }
        for (final ScanProgressListener p : scanProgressListeners) {
            p.finishedProcessing(srcidentifier);
        }
        processed.clear();
    }

    protected void fireClose() {
        for (final ScanProgressListener p : scanProgressListeners) {
            p.close();
        }
    }

    public void addScanner(final CFLintScanner plugin) {
        if (plugin != null) {
            extensions.add(plugin);
            if (plugin instanceof CFLintSet) {
                ((CFLintSet) plugin).setCFLint(this);
            }
        }
    }

    public List getScanners() {
        return extensions;
    }

    public void addExceptionListener(final CFLintExceptionListener exceptionListener) {
        exceptionListeners.add(exceptionListener);
    }

    protected void fireCFLintException(final Throwable e, final String messageCode, final String filename,
            final Integer line, final Integer column, final String functionName, final String expression) {
        for (final CFLintExceptionListener p : exceptionListeners) {
            p.exceptionOccurred(e, messageCode, filename, line, column, functionName, expression);
        }
    }

    public void setShowProgress(final boolean showProgress) {
        this.showProgress = showProgress;
    }

    public void setProgressUsesThread(final boolean progressUsesThread) {
        this.progressUsesThread = progressUsesThread;
    }

    @Override
    public void syntaxError(final Recognizer recognizer, final Object offendingSymbol, int line,
            int charPositionInLine, final String msg, final org.antlr.v4.runtime.RecognitionException re) {
        String expression = null;
        int offset = charPositionInLine;
        int startLine = 0;
        int startOffset = 0;
        final Context context = new Context(currentFile, currentElement, null, true, null, null);
        if (currentElement != null) {
            startOffset = context.offset();
            if(context.startLine() != 1) {
                startLine = currentElement.getSource().getRow(startOffset) - 1;
            }
        }
        if (offendingSymbol instanceof Token && re != null) {
            // grab the first non-comment previous token, which is actually the cause of the syntax error theoretically
            CommonTokenStream tokenStream = (CommonTokenStream)recognizer.getInputStream();
            Token previousToken = tokenStream.get(re.getOffendingToken().getTokenIndex()-1);
            if (previousToken != null) {
                while(previousToken.getChannel() == Token.HIDDEN_CHANNEL && tokenStream.get(previousToken.getTokenIndex()-1) != null) {
                    previousToken = tokenStream.get(previousToken.getTokenIndex()-1);
                }
                line = previousToken.getLine();
                offset = previousToken.getStopIndex();
                expression = previousToken.getText();
            } else {
                expression = re.getOffendingToken().getText();
            }
            if (expression.length() > 50) {
                expression = expression.substring(1, 40) + "...";
            }
        }
        offset +=  startOffset;
        line += startLine;
        if (recognizer instanceof Parser && ((Parser) recognizer).isExpectedToken(CFSCRIPTParser.SEMICOLON)) {
            final ContextMessage cm = new ContextMessage(MISSING_SEMI, expression, null, line, offset);
            reportRule(currentElement, null, context, null, cm);
        } else {
            final ContextMessage cm = new ContextMessage(PARSE_ERROR, expression, null, line, offset);
            reportRule(currentElement, null, context, null, cm);
        }
    }

    @Override
    public void reportAmbiguity(final Parser recognizer, final DFA dfa, final int startIndex, final int stopIndex,
            final boolean exact, final java.util.BitSet ambigAlts, final ATNConfigSet configs) {
        // Empty implementation
    }

    @Override
    public void reportAttemptingFullContext(final Parser recognizer, final DFA dfa, final int startIndex,
            final int stopIndex, final java.util.BitSet conflictingAlts, final ATNConfigSet configs) {
        // Empty implementation
    }

    @Override
    public void reportContextSensitivity(final Parser recognizer, final DFA dfa, final int startIndex,
            final int stopIndex, final int prediction, final ATNConfigSet configs) {
        // Empty implementation
    }

    @Override
    public void reportError(final org.antlr.v4.runtime.RecognitionException re) {
        // Empty implementation
    }

    @Override
    public void reportError(final String[] tokenNames, final org.antlr.v4.runtime.RecognitionException re) {
        // Empty implementation
    }

    @Override
    public void reportError(final org.antlr.v4.runtime.IntStream input,
            final org.antlr.v4.runtime.RecognitionException re, final BitSet follow) {
        // Empty implementation
    }

    public void setStrictIncludes(final boolean strictInclude) {
        this.strictInclude = strictInclude;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy