| package org.checkerframework.checker.i18nformatter; |
| |
| import javax.lang.model.element.AnnotationMirror; |
| import org.checkerframework.checker.formatter.FormatterTreeUtil.Result; |
| import org.checkerframework.checker.i18nformatter.qual.I18nConversionCategory; |
| import org.checkerframework.checker.i18nformatter.qual.I18nInvalidFormat; |
| import org.checkerframework.dataflow.analysis.ConditionalTransferResult; |
| import org.checkerframework.dataflow.analysis.RegularTransferResult; |
| import org.checkerframework.dataflow.analysis.TransferInput; |
| import org.checkerframework.dataflow.analysis.TransferResult; |
| import org.checkerframework.dataflow.cfg.node.MethodInvocationNode; |
| import org.checkerframework.dataflow.expression.JavaExpression; |
| import org.checkerframework.framework.flow.CFAnalysis; |
| import org.checkerframework.framework.flow.CFStore; |
| import org.checkerframework.framework.flow.CFTransfer; |
| import org.checkerframework.framework.flow.CFValue; |
| import org.checkerframework.javacutil.AnnotationBuilder; |
| |
| /** |
| * The transfer function for the Internationalization Format String Checker. |
| * |
| * @checker_framework.manual #i18n-formatter-checker Internationalization Format String Checker |
| */ |
| public class I18nFormatterTransfer extends CFTransfer { |
| |
| public I18nFormatterTransfer(CFAnalysis analysis) { |
| super(analysis); |
| } |
| |
| @Override |
| public TransferResult<CFValue, CFStore> visitMethodInvocation( |
| MethodInvocationNode node, TransferInput<CFValue, CFStore> in) { |
| I18nFormatterAnnotatedTypeFactory atypeFactory = |
| (I18nFormatterAnnotatedTypeFactory) analysis.getTypeFactory(); |
| TransferResult<CFValue, CFStore> result = super.visitMethodInvocation(node, in); |
| I18nFormatterTreeUtil tu = atypeFactory.treeUtil; |
| |
| // If hasFormat is called, make sure that the format string is annotated correctly |
| if (tu.isHasFormatCall(node, atypeFactory)) { |
| CFStore thenStore = result.getRegularStore(); |
| CFStore elseStore = thenStore.copy(); |
| ConditionalTransferResult<CFValue, CFStore> newResult = |
| new ConditionalTransferResult<>(result.getResultValue(), thenStore, elseStore); |
| Result<I18nConversionCategory[]> cats = tu.getHasFormatCallCategories(node); |
| if (cats.value() == null) { |
| tu.failure(cats, "i18nformat.indirect.arguments"); |
| } else { |
| JavaExpression firstParam = JavaExpression.fromNode(node.getArgument(0)); |
| AnnotationMirror anno = atypeFactory.treeUtil.categoriesToFormatAnnotation(cats.value()); |
| thenStore.insertValue(firstParam, anno); |
| } |
| return newResult; |
| } |
| |
| // If isFormat is called, annotate the format string with I18nInvalidFormat |
| if (tu.isIsFormatCall(node, atypeFactory)) { |
| CFStore thenStore = result.getRegularStore(); |
| CFStore elseStore = thenStore.copy(); |
| ConditionalTransferResult<CFValue, CFStore> newResult = |
| new ConditionalTransferResult<>(result.getResultValue(), thenStore, elseStore); |
| JavaExpression firstParam = JavaExpression.fromNode(node.getArgument(0)); |
| AnnotationBuilder builder = new AnnotationBuilder(tu.processingEnv, I18nInvalidFormat.class); |
| // No need to set a value of @I18nInvalidFormat |
| builder.setValue("value", ""); |
| elseStore.insertValue(firstParam, builder.build()); |
| return newResult; |
| } |
| |
| // @I18nMakeFormat that will be used to annotate ResourceBundle.getString() so that when the |
| // getString() method is called, this will check if the given key exist in the translation |
| // file and annotate the result string with the correct format annotation according to the |
| // corresponding key's value. |
| if (tu.isMakeFormatCall(node, atypeFactory)) { |
| Result<I18nConversionCategory[]> cats = tu.makeFormatCallCategories(node, atypeFactory); |
| if (cats.value() == null) { |
| tu.failure(cats, "i18nformat.key.not.found"); |
| } else { |
| AnnotationMirror anno = atypeFactory.treeUtil.categoriesToFormatAnnotation(cats.value()); |
| CFValue newResultValue = |
| analysis.createSingleAnnotationValue(anno, result.getResultValue().getUnderlyingType()); |
| return new RegularTransferResult<>(newResultValue, result.getRegularStore()); |
| } |
| } |
| |
| return result; |
| } |
| } |