Package ro.redeul.google.go.inspection

Source Code of ro.redeul.google.go.inspection.IndexExpressionInspection

package ro.redeul.google.go.inspection;

import org.jetbrains.annotations.NotNull;
import ro.redeul.google.go.GoBundle;
import ro.redeul.google.go.inspection.fix.CastTypeFix;
import ro.redeul.google.go.lang.psi.GoFile;
import ro.redeul.google.go.lang.psi.expressions.GoExpr;
import ro.redeul.google.go.lang.psi.expressions.primary.GoIndexExpression;
import ro.redeul.google.go.lang.psi.typing.*;
import ro.redeul.google.go.lang.psi.visitors.GoRecursiveElementVisitor;
import ro.redeul.google.go.util.GoNumber;

import java.math.BigDecimal;
import java.math.BigInteger;

import static ro.redeul.google.go.lang.psi.utils.GoPsiUtils.getAs;

public class IndexExpressionInspection extends AbstractWholeGoFileInspection {
    @Override
    protected void doCheckFile(@NotNull GoFile file, @NotNull final InspectionResult result) {

        new GoRecursiveElementVisitor() {
            @Override
            public void visitIndexExpression(GoIndexExpression expression) {
                checkIndexExpression(expression, result);
            }


        }.visitFile(file);
    }

    private void checkIndexExpression(GoIndexExpression expression, final InspectionResult result) {
        final GoExpr indexExpr = expression.getIndex();
        if (indexExpr == null)
            return;

        final GoFile goFile = getAs(GoFile.class, indexExpr.getContainingFile());
        if (goFile == null)
            return;

        final GoType[] indexTypes = indexExpr.getType();

        if (indexTypes.length != 1 || indexTypes[0] == null)
            return;
        final GoType indexType = indexTypes[0];

        GoType[] expressionTypes = expression.getBaseExpression().getType();
        if (expressionTypes.length != 1 || expressionTypes[0] == null)
            return;

        final BigInteger intConstant = getIntegerConstant(indexType);

        expressionTypes[0].underlyingType().accept(new UpdatingTypeVisitor<InspectionResult>() {
            @Override
            public void visitArray(GoTypeArray type, InspectionResult result, TypeVisitor<InspectionResult> visitor) {
                checkUnsignedIntegerConstantInRange(indexExpr, indexType, type.getLength(), result);
            }

            @Override
            public void visitSlice(GoTypeSlice type, InspectionResult result, TypeVisitor<InspectionResult> visitor) {
                checkUnsignedIntegerConstantInRange(indexExpr, indexType, Integer.MAX_VALUE, result);
            }

            @Override
            public void visitPrimitive(GoTypePrimitive type, InspectionResult data, TypeVisitor<InspectionResult> visitor) {
                switch (type.getType()) {
                    case String:
                        checkUnsignedIntegerConstantInRange(indexExpr, indexType, Integer.MAX_VALUE, result);
                        break;
                    default:
                        result.addProblem(
                                indexExpr,
                                GoBundle.message("warning.functioncall.type.mismatch", GoTypes.getRepresentation(indexType, goFile))
                        );
                }
            }

            @Override
            public void visitPointer(GoTypePointer type, InspectionResult data, TypeVisitor<InspectionResult> visitor) {
                type.getTargetType().underlyingType().accept(visitor);
            }

            @Override
            public void visitConstant(GoTypeConstant type, InspectionResult data, TypeVisitor<InspectionResult> visitor) {
                switch (type.getKind()) {
                    case String:
                        String stringValue = type.getValueAs(String.class);
                        checkUnsignedIntegerConstantInRange(indexExpr, indexType, stringValue != null ? stringValue.length() : Integer.MAX_VALUE, result);
                    default:
                        result.addProblem(
                                indexExpr,
                                GoBundle.message("warning.functioncall.type.mismatch", GoTypes.getRepresentation(indexType, goFile))
                        );
                }
            }

            @Override
            public void visitMap(GoTypeMap type, InspectionResult data, TypeVisitor<InspectionResult> visitor) {
                GoType keyType = type.getKeyType();
                if (!keyType.isAssignableFrom(indexType)) {
                    result.addProblem(
                            indexExpr,
                            GoBundle.message("warn.index.map.invalid.type",
                                    indexExpr.getText(),
                                    GoTypes.getRepresentation(indexType, goFile),
                                    GoTypes.getRepresentation(keyType, goFile)),
                            new CastTypeFix(indexExpr, keyType));
                }
            }
        }, result);
    }

    private void checkUnsignedIntegerConstantInRange(GoExpr indexExpr, GoType indexType, int length, InspectionResult result) {
        BigInteger integer = getIntegerConstant(indexType);

        if (!(indexType instanceof GoTypeConstant) || integer == null)
            result.addProblem(
                    indexExpr,
                    GoBundle.message("warning.functioncall.type.mismatch", "int"));

        if (integer != null && integer.compareTo(BigInteger.ZERO) < 0)
            result.addProblem(
                    indexExpr,
                    GoBundle.message("warning.index.invalid", integer.longValue(), "(index must be non-negative)"));

        if (integer != null && integer.compareTo(BigInteger.valueOf(length)) > 0)
            result.addProblem(
                    indexExpr,
                    GoBundle.message("warning.index.invalid", integer.longValue(), "(index out of bounds)"));
    }

    private BigInteger getIntegerConstant(GoType indexType) {
        if (!(indexType instanceof GoTypeConstant))
            return null;

        GoTypeConstant constant = (GoTypeConstant) indexType;
        BigDecimal decimal = null;
        BigInteger integer = null;

        if (constant.getKind() == GoTypeConstant.Kind.Complex) {
            GoNumber goNumber = constant.getValueAs(GoNumber.class);
            if (goNumber == null || goNumber.isComplex())
                return null;

            decimal = goNumber.getReal();
        }

        if (decimal != null || constant.getKind() == GoTypeConstant.Kind.Float) {
            decimal = (decimal == null) ? constant.getValueAs(BigDecimal.class) : decimal;
            if (decimal == null)
                return null;

            try {
                integer = decimal.toBigIntegerExact();
            } catch (ArithmeticException e) {
                return null;
            }
        }

        if (integer != null || constant.getKind() == GoTypeConstant.Kind.Integer) {
            integer = (integer == null) ? constant.getValueAs(BigInteger.class) : integer;
        }


        return integer;
    }
}
TOP

Related Classes of ro.redeul.google.go.inspection.IndexExpressionInspection

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.