Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6dc8ba48 authored by Pablo Gamito's avatar Pablo Gamito
Browse files

Collect parsing errors to report them at the end

Bug: 403564102
Flag: EXEMPT code processing tool changes
Test: atest com.android.protolog.tool
Change-Id: Idd3a0623d41b2ebd529ab1dc5e9781ec1165ca5b
parent ac720fbb
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -24,5 +24,5 @@ interface ProtoLogCallProcessor {
        logCallVisitor: ProtoLogCallVisitor?,
        otherCallVisitor: MethodCallVisitor?,
        fileName: String
    ): CompilationUnit
    ): Collection<CodeProcessingException>
}
+42 −29
Original line number Diff line number Diff line
@@ -74,7 +74,7 @@ class ProtoLogCallProcessorImpl(
    }

    fun process(code: CompilationUnit, logCallVisitor: ProtoLogCallVisitor?, fileName: String):
            CompilationUnit {
            Collection<CodeProcessingException> {
        return process(code, logCallVisitor, null, fileName)
    }

@@ -83,7 +83,7 @@ class ProtoLogCallProcessorImpl(
        logCallVisitor: ProtoLogCallVisitor?,
        otherCallVisitor: MethodCallVisitor?,
        fileName: String
    ): CompilationUnit {
    ): Collection<CodeProcessingException> {
        CodeUtils.checkWildcardStaticImported(code, protoLogClassName, fileName)
        CodeUtils.checkWildcardStaticImported(code, protoLogGroupClassName, fileName)

@@ -93,50 +93,63 @@ class ProtoLogCallProcessorImpl(
                protoLogGroupClassName)
        val staticGroupImports = CodeUtils.staticallyImportedMethods(code, protoLogGroupClassName)

        val errors = mutableListOf<CodeProcessingException>()
        code.findAll(MethodCallExpr::class.java)
                .filter { call ->
                    isProtoCall(call, isLogClassImported, staticLogImports)
                }.forEach { call ->
                    val context = ParsingContext(fileName, call)

                    try {
                        val logMethods = LogLevel.entries.map { it.shortCode }
                        if (logMethods.contains(call.name.id)) {
                            // Process a log call
                            if (call.arguments.size < 2) {
                            throw InvalidProtoLogCallException("Method signature does not match " +
                                    "any ProtoLog method: $call", context)
                                errors.add(InvalidProtoLogCallException(
                                    "Method signature does not match " +
                                            "any ProtoLog method: $call", context
                                ))
                                return@forEach
                            }

                        val messageString = CodeUtils.concatMultilineString(call.getArgument(1),
                            context)
                            val messageString = CodeUtils.concatMultilineString(
                                call.getArgument(1),
                                context
                            )
                            val groupNameArg = call.getArgument(0)
                            val groupName =
                            getLogGroupName(groupNameArg, isGroupClassImported,
                                staticGroupImports, fileName)
                                getLogGroupName(
                                    groupNameArg, isGroupClassImported,
                                    staticGroupImports, fileName
                                )
                            if (groupName !in groupMap) {
                            throw InvalidProtoLogCallException("Unknown group argument " +
                                    "- not a ProtoLogGroup enum member: $call", context)
                                errors.add(InvalidProtoLogCallException(
                                    "Unknown group argument " +
                                            "- not a ProtoLogGroup enum member: $call", context
                                ))
                                return@forEach
                            }

                        try {
                            logCallVisitor?.processCall(
                                call, messageString, getLevelForMethodName(
                                    call.name.toString(), call, context
                                ), groupMap.getValue(groupName),
                                context.lineNumber
                            )
                        } catch (e: Throwable) {
                            throw InvalidProtoLogCallException("Error processing log call: $call",
                                context, e)
                        }
                        } else if (call.name.id == "init") {
                            // No processing
                        } else {
                            // Process non-log message calls
                            otherCallVisitor?.processCall(call)
                        }
                    } catch (e: Throwable) {
                        errors.add(InvalidProtoLogCallException(
                            "Error processing log call: $call",
                            context, e
                        ))
                    }
                }
        return code
        return errors
    }

    private fun getLevelForMethodName(
+19 −6
Original line number Diff line number Diff line
@@ -130,14 +130,16 @@ object ProtoLogTool {
                    val outSrc = try {
                        val code = tryParse(text, path)
                        if (containsProtoLogText(text, PROTOLOG_CLASS_NAME)) {
                            transformer.processClass(text, path, packagePath(file, code), code)
                            val (processedText, errors) = transformer.processClass(text, path, packagePath(file, code), code)
                            errors.forEach { injector.reportProcessingError(it) }
                            processedText
                        } else {
                            text
                        }
                    } catch (ex: ParsingException) {
                        // If we cannot parse this file, skip it (and log why). Compilation will
                        // fail in a subsequent build step.
                        injector.reportParseError(ex)
                        injector.reportProcessingError(ex)
                        text
                    }
                    path to outSrc
@@ -405,7 +407,7 @@ object ProtoLogTool {
                        } catch (ex: ParsingException) {
                            // If we cannot parse this file, skip it (and log why). Compilation will
                            // fail in a subsequent build step.
                            injector.reportParseError(ex)
                            injector.reportProcessingError(ex)
                            null
                        }
                    } else {
@@ -466,6 +468,14 @@ object ProtoLogTool {
        try {
            val command = CommandOptions(args)
            invoke(command)

            if (injector.processingErrors.isNotEmpty()) {
                injector.processingErrors.forEachIndexed { index, it ->
                    println("CodeProcessingException " +
                            "(${index + 1}/${injector.processingErrors.size}): \n${it.message}\n")
                }
                exitProcess(1)
            }
        } catch (ex: InvalidCommandException) {
            println("InvalidCommandException: \n${ex.message}\n")
            showHelpAndExit()
@@ -489,12 +499,14 @@ object ProtoLogTool {
    }

    var injector = object : Injector {
        override val processingErrors: MutableList<CodeProcessingException>
            get() = mutableListOf()
        override fun fileOutputStream(file: String) = FileOutputStream(file)
        override fun readText(file: File) = file.readText()
        override fun readLogGroups(jarPath: String, className: String) =
                ProtoLogGroupReader().loadFromJar(jarPath, className)
        override fun reportParseError(ex: ParsingException) {
            println("\n${ex.message}\n")
        override fun reportProcessingError(ex: CodeProcessingException) {
            processingErrors.add(ex)
        }
    }

@@ -502,7 +514,8 @@ object ProtoLogTool {
        fun fileOutputStream(file: String): OutputStream
        fun readText(file: File): String
        fun readLogGroups(jarPath: String, className: String): Map<String, LogGroup>
        fun reportParseError(ex: ParsingException)
        fun reportProcessingError(ex: CodeProcessingException)
        val processingErrors: Collection<CodeProcessingException>
    }
}

+3 −3
Original line number Diff line number Diff line
@@ -66,13 +66,13 @@ class SourceTransformer(
        packagePath: String,
        compilationUnit: CompilationUnit =
            StaticJavaParser.parse(code)
    ): String {
    ): Pair<String, Collection<CodeProcessingException>> {
        this.path = path
        this.packagePath = packagePath
        processedCode = code.split('\n').toMutableList()
        offsets = IntArray(processedCode.size)
        protoLogCallProcessor.process(compilationUnit, protoLogCallVisitor, otherCallVisitor, path)
        return processedCode.joinToString("\n")
        val processingErrors = protoLogCallProcessor.process(compilationUnit, protoLogCallVisitor, otherCallVisitor, path)
        return Pair(processedCode.joinToString("\n"), processingErrors)
    }

    private val protoLogImplClassNode =
+42 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.protolog.tool

import com.android.protolog.tool.ProtoLogTool.PROTOLOG_IMPL_SRC_PATH
import com.android.protolog.tool.ProtoLogTool.injector
import com.google.common.truth.Truth
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
@@ -102,6 +103,42 @@ class EndToEndTest {
        """.trimIndent())
    }

    @Test
    fun e2e_transform_withErrors() {
        val srcs = mapOf(
            "frameworks/base/org/example/Example.java" to """
                    package org.example;
                    import com.android.internal.protolog.ProtoLog;
                    import static com.android.internal.protolog.ProtoLogGroup.GROUP;

                    class Example {
                        void method() {
                            String argString = "hello";
                            int argInt = 123;
                            ProtoLog.d(GROUP, "Invalid format: %s %d %9 %", argString, argInt);
                        }
                    }
                """.trimIndent())
        val output = run(
            srcs = srcs,
            logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
            commandOptions = CommandOptions(arrayOf("transform-protolog-calls",
                "--protolog-class", "com.android.internal.protolog.ProtoLog",
                "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
                "--loggroups-jar", "not_required.jar",
                "--viewer-config-file-path", "not_required.pb",
                "--output-srcjar", "out.srcjar",
                "frameworks/base/org/example/Example.java"))
        )
        val outSrcJar = assertLoadSrcJar(output, "out.srcjar")
        // No change to source code on failure to process
        Truth.assertThat(outSrcJar["frameworks/base/org/example/Example.java"])
            .contains(srcs["frameworks/base/org/example/Example.java"])

        Truth.assertThat(injector.processingErrors).hasSize(1)
        Truth.assertThat(injector.processingErrors.first().message).contains("Invalid format")
    }

    private fun assertLoadSrcJar(
        outputs: Map<String, ByteArray>,
        path: String
@@ -172,7 +209,11 @@ class EndToEndTest {
            override fun readLogGroups(jarPath: String, className: String) = mapOf(
                    logGroup.name to logGroup)

            override fun reportParseError(ex: ParsingException) = throw AssertionError(ex)
            override fun reportProcessingError(ex: CodeProcessingException) {
                processingErrors.add(ex)
            }

            override val processingErrors: MutableList<CodeProcessingException> = mutableListOf()
        }

        ProtoLogTool.invoke(commandOptions)
Loading