detekt / detekt

Static code analysis for Kotlin
https://detekt.dev
Apache License 2.0
6.09k stars 755 forks source link

SARIF export support #3045

Closed dector closed 3 years ago

dector commented 3 years ago

GitHub code scanning allows uploading SARIF (Static Analysis Results Interchange Format) files.

Kotlin language is not yet supported in current (beta) version. But it would be nice to be prepared in advance. :)

Useful resources:

BraisGabin commented 3 years ago

I didn't know about Sarif it seems like a report format that we should support.

schalkms commented 3 years ago

Yeah, I agree. That's another useful report format. @dector do you have resources to help us here? The docs would be a good starting point. https://detekt.github.io/detekt/extensions.html#custom-reports

dector commented 3 years ago

@BraisGabin That's Microsoft-developed format and they used it in GitHub code scan. It's JSON-based and seems quite simple.

@schalkms Added useful links.

dector commented 3 years ago

I might prepare PoC as a topic-started but not in nearest week or two. But I guess we have time still.

dector commented 3 years ago

Here some tryouts: https://github.com/dector/detekt/blob/sarif/detekt-report-sarif/src/test/kotlin/io/github/detekt/report/xml/SarifOutputFormatSpec.kt#L68

I guess I can continue with this task piece-by-piece.

Are there some preferences for JSON serialization? Should we use kotlinx.serialization for the sake of multi-platform support?

arturbosch commented 3 years ago

Here some tryouts: https://github.com/dector/detekt/blob/sarif/detekt-report-sarif/src/test/kotlin/io/github/detekt/report/xml/SarifOutputFormatSpec.kt#L68

I guess I can continue with this task piece-by-piece.

Are there some preferences for JSON serialization? Should we use kotlinx.serialization for the sake of multi-platform support?

Ironically one week before you opened this PR, we had a small discussion about supporting sarif format in the company I work at :).

Looks good. detekt does not yet support relative paths which will be needed to upload such an format to GitHub if I see this correctlycorrectly (detekt/detekt#2492).

If kotlinx.serialization is available in mavenCentral lets go with it.

arturbosch commented 3 years ago

@BraisGabin I think you edited my answer somehow xD

The sarif format allows to add custom meta data about rules. That is good, I would like to remove the txt format and make the sarif format the default for 2.0 with the issue signature as meta data.

BraisGabin commented 3 years ago

🀦🀦🀦🀦🀦 So sorry! I think that I clicked "Edit" instead of "Quote" and I end up writing my comment as an edit of yours >_<.

I just recovered your previous message. I didn't know that I could edit the messages of other people!

My previous message was:

If kotlinx.serialization is available in mavenCentral lets go with it.

If it's not I recommend moshi. It's really nice (I really don't care).

Just "dreaming": once we have this done we can even split the SARIF mappers from detekt. The mapper could be useful for ktlint too (for example).

dector commented 3 years ago

Awesome! Thanks @arturbosch and @BraisGabin !

I thought about kotlinx.serialization because of it's multiplatform nature (e.g. will not create additional friction on building detekt as a native binary tool in the future). But I'm happy to use any good tool (including Moshi) for v1.

Just "dreaming": once we have this done we can even split the SARIF mappers from detekt. The mapper could be useful for ktlint too (for example).

I can implement SARIF library as a separate project - not a problem at all. So it'll be easier to reuse it in other tools too.

drewhannay commented 3 years ago

@dector it would be great if we had a way to generate Kotlin SARIF model bindings directly from the JSON schema

I've been looking into this as well, but it doesn't seem like there are any great bindings yet. SpotBugs added a SARIF report with some Java bindings, so that might be worth looking at. Ideally the SARIF team would add an "official" Java or Kotlin set of bindings.

dector commented 3 years ago

it would be great if we had a way to generate Kotlin SARIF model bindings directly from the JSON schema

I agree. However (and most probably) Kotlinizing generated binding will require additional effort as there are no ready-to-use generators for this.

Ideally the SARIF team would add an "official" Java or Kotlin set of bindings.

Unfortunately Microsoft is here at it's best. 😞 There are few different tools for C# but no implementations for other languages. There are numerous mistakes in specification examples. Existing online tools are rough, buggy and have very limited functionality. :)

schalkms commented 3 years ago

Thanks for the productive discussion, guys! πŸ‘

Are there some preferences for JSON serialization? Should we use kotlinx.serialization for the sake of multi-platform support?

Definitely, we should choose a mature and maintained package. kotlinx.serialization would in my opinion fulfill this need.

I can implement SARIF library as a separate project - not a problem at all. So it'll be easier to reuse it in other tools too.

I would prefer using the detekt report API for the first prototype. I don't think we should make it more complex than necessary and dependent on other packages.

greysteil commented 3 years ago

πŸ‘‹ Weighing in from the GitHub code scanning team (I'm one of the PMs responsible for what we're doing in the space).

We'd love to support displaying results from detekt within code scanning, and to include detekt as a card in the code scanning setup experience (that link will only work for folks with write access to this repo). If it's helpful I can ask the team on our side to answer any questions you have on what's required - the simple answer is support for outputting SARIF and a version of the detekt-all Action that uploads results to code scanning.

Thanks for all your work on detekt!

BraisGabin commented 3 years ago

Write access or administration access? I have write access but I get a 404

greysteil commented 3 years ago

Updated to actually be the right link! 🀦

arturbosch commented 3 years ago

Hm, I'm still seeing a 404 for https://github.com/detekt/detekt/security/code-scanning/setup

greysteil commented 3 years ago

Ah, I know what this is - we’re still in beta so need to flip the feature flag for this repo. One moment

greysteil commented 3 years ago

You should have access now.

arturbosch commented 3 years ago

You should have access now.

Thanks it works for me now.

2020-09-24-212417_590x291_scrot

I've found https://help.semmle.com/codeql/codeql-cli/reference/sarif-overview.html which seems to describe the tiny part of sarif which is required to get this to work :).

I try to take a look at https://github.com/dector/sarif-kotlin from @dector in the next days.

jhutchings1 commented 3 years ago

@arturbosch Excited to see Detekt adding SARIF support! A couple of resources for you as you plan your integration:

Ping us if you encounter any issues!

arturbosch commented 3 years ago

I've extended @dector repository with model generation based on the json schema - https://github.com/arturbosch/sarif-kotlin.

However I'm unsure about sarif's licensing - https://github.com/oasis-tcs/sarif-spec/blob/master/LICENSE.md.

Therefore I have a script to download the json schema on demand to generate the models. Also I'm not sure if it's licencing allows to generate models by Non-OASIS/Microsoft members.

greysteil commented 3 years ago

Hmmm, that license isn't as clear as it could be. The readme says the intent is that everything in that repo should be free for anyone to use, but it looks like OASIS could be clearer on the legals.

@lcartey - you were a co-author on the SARIF spec, anything we can do to make this more straightforward, and to give @arturbosch and the team certainty they're not violating any licenses? Also cc @tom-corbett in case he can help.

jhutchings1 commented 3 years ago

cc: @michaelcfanning as well

arturbosch commented 3 years ago

Hey @dector, are you interested in maintaining the sarif library in the long term? I could create a PR with my additions.

My current changes include generating Java models based on the json scheme and use moshi as the json writer. To meet the requirements of plain Java developers, I moved Kotlin to the test dependencies. No extra Kotlin deps needed ;).

If you are okay with it, I would move my fork to the detekt organization and release an initial version under the detekt umbrella.

dector commented 3 years ago

@arturbosch I guess SARIF support should be part of the detekt family! πŸ˜ƒ However, I though that it should be Kotlin-first library. SARIF team is planning Java support (their website says "Coming soon"). So first-class Kotlin support (as I see it) includes:

I clearly see the point of generating POJOs on the first stages but it would be nice to move to Kotlin-first library with nice DSL later. Very interested to hear what do you think about that.

michaelcfanning commented 3 years ago

Hello, very interested to see this conversation in flight. The SARIF OASIS JSON schema is absolutely available for arbitrary use. I will ask @lgolding, SARIF project editor, to help make this more explicit in that repo.

We definitely appreciate the feedback on spec bugs. Separately, we have a new effort to provide SARIF assistance in a more practical, consumable form, this content is at https://github.com/microsoft/sarif-tutorials. It is very much a work in progress, but @lgolding is very responsive to specific requests for samples/guidance if you have them.

In terms of Java OM, historically, I have heard engineers have used jsonschema2pojo successfully against the SARIF schema (which again is free to use in this way or any other application). Sounds like @arturbosch has done something similar as a step in his process. We have not shipped the output of this tool, thinking that an actual Java SDK for SARIF probably would need more substance to it.

@lgolding and I are available at any time to review/comment on prospective SARIF, or answer any questions at all. If anyone would like to make email contact, my ms alias is mikefan.

ghost commented 3 years ago

@michaelcfanning Thanks for bringing this to my attention. You're right that the license in the spec repo is not as clear as it should be. I will get in touch with OASIS to see how it can be clarified.

Everyone, feel free to contact me directly with any questions at v-lgold@microsoft.com. We can video chat on Teams as well.

schalkms commented 3 years ago

Hello guys, thanks for the support. We really appreciate your help. πŸ™‚ By the way, is there a roadmap for the Java support? I'm not sure whether reinventing the wheel by implementing the SARIF support in Kotlin is the right approach here (for detekt)? Thanks in advance.

ghost commented 3 years ago

@schalkms I'm not aware of a roadmap for standardizing Java support. @michaelcfanning?

There's certainly a problem with providing language bindings for standardized object models:

You know it occurs to me that there's a need for a meta-standard that says something like "Given a JSON schema for an object model, here's how you define the language bindings for the following languages" -- and then somehow, standards bodies could plug individual "meta-bindings" into the standard (here's how you should generate ECMA-script bindings from JSON schemas; here's how you should generate Python bindings, etc.)

But I digress :-)

michaelcfanning commented 3 years ago

We would be glad to produce and publish 'official' POJOs deriving from the final SARIF 2.1 JSON schema if it would be valuable. We do not have a current MS roadmap for building out Java SARIF support, simply because the demand has not been there to-date.

If that changes, I would be very glad look into getting more Microsoft attention/resources on this gap. The SARIF SDK (which is nearly completely dedicated to C# support, reflecting its prevalence internally) contains a lot of critical helpers for reading, writing, baselining SARIF, etc. That support has been key for uptake. If there's significant SARIF processing from Java clients, we'd want similar support. The C# SDK could be a leg up, as many patterns/approaches would be straightforward to recode in Java.

MS has also sponsored creation of SARIF-driven viewers in Visual Studio and VS Code. Expanding into other important IDEs might also make sense.

If you have thoughts on how to continue this higher-level conversation, let me know. I could collect additional participants on the MS side who help design/drive our internal Java adoption who would be glad to weigh in.

ghost commented 3 years ago

And I forgot to mention that if you have any concerns about using the schema from the OASIS site, the open-source SARIF SDK contains a copy you're welcome to use: https://github.com/microsoft/sarif-sdk/blob/master/src/Sarif/Schemata/sarif-2.1.0-rtm.5.json.

Ignore the "rtm.5" in the file name; this is the final, final version.

EDIT The contents of the SDK, including the schema, are unambiguously licensed under the MIT license.

I'm touching base with Chet Ensign of OASIS to see about clarifying their license language to make it clearer to end users of the standard (as opposed to contributors to the standard, to whom the current language seems targeted).

OASIS-OP-Admin commented 3 years ago

@lgolding and @michaelcfanning and all - with respect to the license question:

Thank you for the observation about the lack of clarity in the LICENSE.md file's text. I had not looked at it from the perspective of users of the specifications. I'll review that with our corporate council and try to improve the language.

Meanwhile - since lawyer = time - let me here reassure you all that yes - OASIS absolutely wants you to be able to use the specifications with as few strings attached as possible.

Specifically, as documented in the TC website and in the specifications themselves, the work of the SARIF TC is governed by the OASIS IPR policy (https://www.oasis-open.org/policies-guidelines/ipr), specifically the requirements of our RF on RAND IPR mode. (https://www.oasis-open.org/committees/sarif/ipr.php)

The particular commitments there are explained in https://www.oasis-open.org/policies-guidelines/ipr#RF-Mode-Common. Cutting through the dense legalize, let me draw your attention to this clause (editing for brevity):

For an OASIS Standards Final Deliverable developed by an RF Mode TC... each Obligated Party in such TC hereby covenants that, upon request... it will grant to any OASIS Party or third party: a nonexclusive, worldwide, non-sublicensable, perpetual patent license... to make, have made, use, market, import, offer to sell, and sell, and to otherwise directly or indirectly distribute (a) Licensed Products that implement such OASIS Standards Final Deliverable...

In other words, the members of the TC that produced the SARIF schemas (the 'Obligated Parties') grant users like yourselves the right to use it in your own products.

As I said, I'll work on improving our LICENSE.md text to reflect this. I hope, however, that this gives you the confidence that you can use it now.

arturbosch commented 3 years ago

FYI @dector @schalkms @lgolding @michaelcfanning @greysteil

I went with generating POJO's via the jsonschema2pojo project as it is the fastest and easiest way to progress on implementing sarif support for detekt. We can easily swap out the sarif4j implementation with the official MS version when it will be available. No reinventing the wheel or investing time into an idiomatic Kotlin dsl :) . Just the testcase is in Kotlin.

There is an open issue for supporting Kotlin models - https://github.com/joelittlejohn/jsonschema2pojo/issues/593.

I finished a working prototype of a detekt report extension (https://github.com/detekt/detekt/pull/3132) which the sarif vscode extension can import. The next step is to wait until we can publish our sarif models to maven central and try out the Github integration.

ghost commented 3 years ago

@arturbosch I'd be happy to look at the SARIF you're generating and provide feedback. You can also drag your files onto the online SARIF validator.

michaelcfanning commented 3 years ago

Be sure to select the 'GitHub Ingestion Rules' on the validator site, which inspects the SARIF for some special requirements for integration.

arturbosch commented 3 years ago

@lgolding @michaelcfanning thanks for the tips!

The validator is happy with following file: detekt.txt Note: I had to rename it to .txt for uploading to Github.

2020-10-17-212644_754x642_scrot

ghost commented 3 years ago

Hi @arturbosch, thanks for the sample! It's always helpful to see SARIF from a new tool because it helps us refine our validation rules and our tools.

I haven't reviewed it in complete detail, but here are a few comments and suggestions:

@michaelcfanning: We should have a validation rule for this.

@michaelcfanning: We should have a validation rule for this.

This property can be used by tools that provide an online viewing experience for the results they generate. This experience might be specifically designed to display the results from that tool, as opposed to a generic SARIF viewer that displays results from any tool that produces SARIF.

@michaelcfanning: We should fix the online validator so it pretty-prints the input file (if it isn't already) before validating it. Otherwise it displays the whole file on one line.

I hope this helps! I'll be happy to continue to work with you to refine the SARIF. It won't take long; I've done this with many teams both inside and outside of Microsoft.

josepalafox commented 3 years ago

Hi I just wanted to add that I found this action in our marketplace for detekt that may be re-usable once there's sarif support: https://github.com/marketplace/actions/detekt-all

arturbosch commented 3 years ago

Hi @lgolding

I've found time to get back to this and incorporated all your feedback; thanks for that!

All default values now get stripped. The online validation passes and the vscode-viewer allows to jump to that issue and marks it red.

We do not have the access to the fullDescription of our rules programmatically, so I just added a shortDescription to the reportingDescriptors. This should be enough for the start?

Hi @arturbosch, thanks for the sample! It's always helpful to see SARIF from a new tool because it helps us refine our validation rules and our tools.

I haven't reviewed it in complete detail, but here are a few comments and suggestions:

  • Although JSON is not order-sensitive, the spec recommends putting the version property first so that consumers can sniff the version before parsing the entire file. Actually I suggest putting it after $schema because I'm not sure whether JSON-schema-sensitive IDEs expect $schema to come first.
  • Many properties are optional, so if their value is an empty array, you can omit them to reduce file size. Examples are inlineExternalProperties, run.addresses, run.artifacts, result.relatedLocations, result.stacks, reportingDescriptor.deprecatedGuids, and many others.

@michaelcfanning: We should have a validation rule for this.

  • Integer properties with their default value of -1 should be omitted. The spec isn't as clear as it should be here. These values are required to be non-negative in the format, and the spec says that if they are absent, then the consumer should treat them as if they had the value -1 (so that the consumer can tell that the property wasn't provided). "-1" isn't actually supposed to appear in the file.

@michaelcfanning: We should have a validation rule for this.

  • run.newlineSequences also has its default value (a non-empty array, this time) and so can be omitted.
  • In the next version of the SARIF SDK, the validator will warn against beginning URIs with a leading /, even though strictly speaking /home/x/y/z is a valid relative URI reference. I can explain more about this issue offline.

Should we use an absolute path starting with file:// here instead?

  • I see that in the results, hostedViewerUri has the same value as the result location's URI, which isn't right. hostedViewerUri is supposed to contain "an absolute URI at which the result can be viewed". The spec goes on to explain:

This property can be used by tools that provide an online viewing experience for the results they generate. This experience might be specifically designed to display the results from that tool, as opposed to a generic SARIF viewer that displays results from any tool that produces SARIF.

  • Consider providing pretty-printed (indented) output for readability, or at least making the "minified" form a command line option rather than the default.

@michaelcfanning: We should fix the online validator so it pretty-prints the input file (if it isn't already) before validating it. Otherwise it displays the whole file on one line.

I hope this helps! I'll be happy to continue to work with you to refine the SARIF. It won't take long; I've done this with many teams both inside and outside of Microsoft.

Please see this new version of the report we generate:

{
  "$schema" : "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
  "version" : "2.1.0",
  "runs" : [ {
    "tool" : {
      "driver" : {
        "guid" : "022ca8c2-f6a2-4c95-b107-bb72c43263f3",
        "name" : "detekt",
        "organization" : "detekt",
        "fullName" : "detekt",
        "version" : "1.14.2",
        "semanticVersion" : "1.14.2",
        "downloadUri" : "https://github.com/detekt/detekt/releases/download/v1.14.2/detekt",
        "informationUri" : "https://detekt.github.io/detekt",
        "rules" : [ {
          "id" : "detekt.empty-blocks.EmptyWhileBlock",
          "name" : "EmptyWhileBlock",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptywhileblock"
        }, {
          "id" : "detekt.style.OptionalAbstractKeyword",
          "name" : "OptionalAbstractKeyword",
          "shortDescription" : {
            "text" : "Unnecessary abstract modifier in interface"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#optionalabstractkeyword"
        }, {
          "id" : "detekt.complexity.MethodOverloading",
          "name" : "MethodOverloading",
          "shortDescription" : {
            "text" : "Methods which are overloaded often might be harder to maintain. Furthermore, these methods are tightly coupled. Refactor these methods and try to use optional parameters."
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#methodoverloading"
        }, {
          "id" : "detekt.empty-blocks.EmptyFinallyBlock",
          "name" : "EmptyFinallyBlock",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptyfinallyblock"
        }, {
          "id" : "detekt.comments.CommentOverPrivateFunction",
          "name" : "CommentOverPrivateFunction",
          "shortDescription" : {
            "text" : "Comments for private functions should be avoided. Prefer giving the function an expressive name. Split it up in smaller, self-explaining functions if necessary."
          },
          "helpUri" : "https://detekt.github.io/detekt/comments.html#commentoverprivatefunction"
        }, {
          "id" : "detekt.complexity.ComplexMethod",
          "name" : "ComplexMethod",
          "shortDescription" : {
            "text" : "Prefer splitting up complex methods into smaller, easier to understand methods."
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#complexmethod"
        }, {
          "id" : "detekt.potential-bugs.UnnecessaryNotNullOperator",
          "name" : "UnnecessaryNotNullOperator",
          "shortDescription" : {
            "text" : "Unnecessary not-null unary operator (!!) detected."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#unnecessarynotnulloperator"
        }, {
          "id" : "detekt.potential-bugs.Deprecation",
          "name" : "Deprecation",
          "shortDescription" : {
            "text" : "Deprecated elements should not be used."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#deprecation"
        }, {
          "id" : "detekt.style.TrailingWhitespace",
          "name" : "TrailingWhitespace",
          "shortDescription" : {
            "text" : "Checks which lines end with a whitespace."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#trailingwhitespace"
        }, {
          "id" : "detekt.style.EqualsOnSignatureLine",
          "name" : "EqualsOnSignatureLine",
          "shortDescription" : {
            "text" : "Equals signs for expression style functions should be on the same line as the signature"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#equalsonsignatureline"
        }, {
          "id" : "detekt.style.DataClassShouldBeImmutable",
          "name" : "DataClassShouldBeImmutable",
          "shortDescription" : {
            "text" : "Data classes should mainly be immutable and should not have any side effects. (To copy an object altering some of its properties use the copy function)"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#dataclassshouldbeimmutable"
        }, {
          "id" : "detekt.potential-bugs.UnnecessarySafeCall",
          "name" : "UnnecessarySafeCall",
          "shortDescription" : {
            "text" : "Unnecessary safe call operator detected."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#unnecessarysafecall"
        }, {
          "id" : "detekt.potential-bugs.UnreachableCode",
          "name" : "UnreachableCode",
          "shortDescription" : {
            "text" : "Unreachable code detected. This code should be removed"
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#unreachablecode"
        }, {
          "id" : "detekt.empty-blocks.EmptyKtFile",
          "name" : "EmptyKtFile",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptyktfile"
        }, {
          "id" : "detekt.style.UnnecessaryApply",
          "name" : "UnnecessaryApply",
          "shortDescription" : {
            "text" : "The `apply` usage is unnecessary"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#unnecessaryapply"
        }, {
          "id" : "detekt.style.PreferToOverPairSyntax",
          "name" : "PreferToOverPairSyntax",
          "shortDescription" : {
            "text" : "Pair was created using the Pair constructor, using the to syntax is preferred."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#prefertooverpairsyntax"
        }, {
          "id" : "detekt.style.LoopWithTooManyJumpStatements",
          "name" : "LoopWithTooManyJumpStatements",
          "shortDescription" : {
            "text" : "The loop contains more than one break or continue statement. The code should be refactored to increase readability."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#loopwithtoomanyjumpstatements"
        }, {
          "id" : "detekt.naming.PackageNaming",
          "name" : "PackageNaming",
          "shortDescription" : {
            "text" : "Package names should match the naming convention set in the configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#packagenaming"
        }, {
          "id" : "detekt.comments.UndocumentedPublicProperty",
          "name" : "UndocumentedPublicProperty",
          "shortDescription" : {
            "text" : "Public properties require documentation."
          },
          "helpUri" : "https://detekt.github.io/detekt/comments.html#undocumentedpublicproperty"
        }, {
          "id" : "detekt.naming.FunctionMaxLength",
          "name" : "FunctionMaxLength",
          "shortDescription" : {
            "text" : "Function names should not be longer than the maximum set in the project configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#functionmaxlength"
        }, {
          "id" : "detekt.empty-blocks.EmptySecondaryConstructor",
          "name" : "EmptySecondaryConstructor",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptysecondaryconstructor"
        }, {
          "id" : "detekt.style.ForbiddenComment",
          "name" : "ForbiddenComment",
          "shortDescription" : {
            "text" : "Flags a forbidden comment. Defaults values are TODO:, FIXME: or STOPSHIP:"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#forbiddencomment"
        }, {
          "id" : "detekt.potential-bugs.UselessPostfixExpression",
          "name" : "UselessPostfixExpression",
          "shortDescription" : {
            "text" : "The incremented or decremented value is unused. This value is replaced with the original value."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#uselesspostfixexpression"
        }, {
          "id" : "detekt.exceptions.NotImplementedDeclaration",
          "name" : "NotImplementedDeclaration",
          "shortDescription" : {
            "text" : "The NotImplementedDeclaration should only be used when a method stub is necessary. This defers the development of the functionality of this function. Hence, the NotImplementedDeclaration should only serve as a temporary declaration. Before releasing, this type of declaration should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#notimplementeddeclaration"
        }, {
          "id" : "detekt.potential-bugs.ImplicitUnitReturnType",
          "name" : "ImplicitUnitReturnType",
          "shortDescription" : {
            "text" : "Functions using expression statements have an implicit return type.\nChanging the type of the expression accidentally, changes the function return type.\nThis may lead to backward incompatibility.\nUse a block statement to make clear this function will never return a value."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#implicitunitreturntype"
        }, {
          "id" : "detekt.style.ClassOrdering",
          "name" : "ClassOrdering",
          "shortDescription" : {
            "text" : "Class contents should be in this order: Property declarations/initializer blocks; secondary constructors; method declarations then companion objects."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#classordering"
        }, {
          "id" : "detekt.exceptions.InstanceOfCheckForException",
          "name" : "InstanceOfCheckForException",
          "shortDescription" : {
            "text" : "Instead of checking for a general exception type and checking for a specific exception type use multiple catch blocks."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#instanceofcheckforexception"
        }, {
          "id" : "detekt.style.UnnecessaryInheritance",
          "name" : "UnnecessaryInheritance",
          "shortDescription" : {
            "text" : "The extended super type is unnecessary."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#unnecessaryinheritance"
        }, {
          "id" : "detekt.empty-blocks.EmptyFunctionBlock",
          "name" : "EmptyFunctionBlock",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptyfunctionblock"
        }, {
          "id" : "detekt.naming.VariableMaxLength",
          "name" : "VariableMaxLength",
          "shortDescription" : {
            "text" : "Variable names should not be longer than the maximum set in the configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#variablemaxlength"
        }, {
          "id" : "detekt.potential-bugs.HasPlatformType",
          "name" : "HasPlatformType",
          "shortDescription" : {
            "text" : "Platform types must be declared explicitly in public APIs."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#hasplatformtype"
        }, {
          "id" : "detekt.style.NewLineAtEndOfFile",
          "name" : "NewLineAtEndOfFile",
          "shortDescription" : {
            "text" : "Checks whether files end with a line separator."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#newlineatendoffile"
        }, {
          "id" : "detekt.style.RedundantVisibilityModifierRule",
          "name" : "RedundantVisibilityModifierRule",
          "shortDescription" : {
            "text" : "Checks for redundant visibility modifiers."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#redundantvisibilitymodifierrule"
        }, {
          "id" : "detekt.style.OptionalUnit",
          "name" : "OptionalUnit",
          "shortDescription" : {
            "text" : "Return type of 'Unit' is unnecessary and can be safely removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#optionalunit"
        }, {
          "id" : "detekt.naming.InvalidPackageDeclaration",
          "name" : "InvalidPackageDeclaration",
          "shortDescription" : {
            "text" : "Kotlin source files should be stored in the directory corresponding to its package statement."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#invalidpackagedeclaration"
        }, {
          "id" : "detekt.style.UntilInsteadOfRangeTo",
          "name" : "UntilInsteadOfRangeTo",
          "shortDescription" : {
            "text" : "'..' call can be replaced with 'until'"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#untilinsteadofrangeto"
        }, {
          "id" : "detekt.style.OptionalWhenBraces",
          "name" : "OptionalWhenBraces",
          "shortDescription" : {
            "text" : "Optional braces in when expression"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#optionalwhenbraces"
        }, {
          "id" : "detekt.style.ProtectedMemberInFinalClass",
          "name" : "ProtectedMemberInFinalClass",
          "shortDescription" : {
            "text" : "Member with protected visibility in final class is private. Consider using private or internal as modifier."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#protectedmemberinfinalclass"
        }, {
          "id" : "detekt.style.UseArrayLiteralsInAnnotations",
          "name" : "UseArrayLiteralsInAnnotations",
          "shortDescription" : {
            "text" : "Array literals '[...]' should be preferred as they are more readable than 'arrayOf(...)' expressions."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#usearrayliteralsinannotations"
        }, {
          "id" : "detekt.coroutines.GlobalCoroutineUsage",
          "name" : "GlobalCoroutineUsage",
          "shortDescription" : {
            "text" : "Usage of GlobalScope instance is highly discouraged"
          },
          "helpUri" : "https://detekt.github.io/detekt/coroutines.html#globalcoroutineusage"
        }, {
          "id" : "detekt.exceptions.ThrowingNewInstanceOfSameException",
          "name" : "ThrowingNewInstanceOfSameException",
          "shortDescription" : {
            "text" : "Avoid catch blocks that rethrow a caught exception wrapped inside a new instance of the same exception."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#throwingnewinstanceofsameexception"
        }, {
          "id" : "detekt.style.ForbiddenVoid",
          "name" : "ForbiddenVoid",
          "shortDescription" : {
            "text" : "`Unit` should be used instead of `Void`."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#forbiddenvoid"
        }, {
          "id" : "detekt.potential-bugs.RedundantElseInWhen",
          "name" : "RedundantElseInWhen",
          "shortDescription" : {
            "text" : "Check for redundant `else` case in `when` expression when used as statement."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#redundantelseinwhen"
        }, {
          "id" : "detekt.potential-bugs.ExplicitGarbageCollectionCall",
          "name" : "ExplicitGarbageCollectionCall",
          "shortDescription" : {
            "text" : "Don't try to be smarter than the JVM. Your code should work independently if the garbage collector is disabled or not. If you face memory issues, try tuning the JVM options instead of relying on code itself."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#explicitgarbagecollectioncall"
        }, {
          "id" : "detekt.style.UtilityClassWithPublicConstructor",
          "name" : "UtilityClassWithPublicConstructor",
          "shortDescription" : {
            "text" : "The class declaration is unnecessary because it only contains utility functions. An object declaration should be used instead."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#utilityclasswithpublicconstructor"
        }, {
          "id" : "detekt.exceptions.ExceptionRaisedInUnexpectedLocation",
          "name" : "ExceptionRaisedInUnexpectedLocation",
          "shortDescription" : {
            "text" : "This method is not expected to throw exceptions. This can cause weird behavior."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#exceptionraisedinunexpectedlocation"
        }, {
          "id" : "detekt.potential-bugs.IteratorHasNextCallsNextMethod",
          "name" : "IteratorHasNextCallsNextMethod",
          "shortDescription" : {
            "text" : "The hasNext() method of an Iterator implementation should not call the next() method. The state of the iterator should not be changed inside the hasNext() method. The hasNext() method is not supposed to have any side effects."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#iteratorhasnextcallsnextmethod"
        }, {
          "id" : "detekt.style.UnnecessaryParentheses",
          "name" : "UnnecessaryParentheses",
          "shortDescription" : {
            "text" : "Unnecessary parentheses don't add any value to the code and should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#unnecessaryparentheses"
        }, {
          "id" : "detekt.style.ExpressionBodySyntax",
          "name" : "ExpressionBodySyntax",
          "shortDescription" : {
            "text" : "Functions with exact one statement, the return statement, can be rewritten with ExpressionBodySyntax."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#expressionbodysyntax"
        }, {
          "id" : "detekt.empty-blocks.EmptyClassBlock",
          "name" : "EmptyClassBlock",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptyclassblock"
        }, {
          "id" : "detekt.potential-bugs.NullableToStringCall",
          "name" : "NullableToStringCall",
          "shortDescription" : {
            "text" : "This call may return the string \"null\""
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#nullabletostringcall"
        }, {
          "id" : "detekt.style.UnnecessaryAbstractClass",
          "name" : "UnnecessaryAbstractClass",
          "shortDescription" : {
            "text" : "An abstract class is unnecessary and can be refactored. An abstract class should have both abstract and concrete properties or functions. An abstract class without a concrete member can be refactored to an interface. An abstract class without an abstract member can be refactored to a concrete class."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#unnecessaryabstractclass"
        }, {
          "id" : "detekt.potential-bugs.ImplicitDefaultLocale",
          "name" : "ImplicitDefaultLocale",
          "shortDescription" : {
            "text" : "Implicit default locale used for string processing. Consider using explicit locale."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#implicitdefaultlocale"
        }, {
          "id" : "detekt.performance.SpreadOperator",
          "name" : "SpreadOperator",
          "shortDescription" : {
            "text" : "In most cases using a spread operator causes a full copy of the array to be created before calling a method. This may result in a performance penalty."
          },
          "helpUri" : "https://detekt.github.io/detekt/performance.html#spreadoperator"
        }, {
          "id" : "detekt.style.ForbiddenPublicDataClass",
          "name" : "ForbiddenPublicDataClass",
          "shortDescription" : {
            "text" : "The data classes are bad for the binary compatibility in public APIs. Avoid to use it."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#forbiddenpublicdataclass"
        }, {
          "id" : "detekt.exceptions.ThrowingExceptionsWithoutMessageOrCause",
          "name" : "ThrowingExceptionsWithoutMessageOrCause",
          "shortDescription" : {
            "text" : "A call to the default constructor of an exception was detected. Instead one of the constructor overloads should be called. This allows to provide more meaningful exceptions."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#throwingexceptionswithoutmessageorcause"
        }, {
          "id" : "detekt.style.SafeCast",
          "name" : "SafeCast",
          "shortDescription" : {
            "text" : "Safe cast instead of if-else-null"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#safecast"
        }, {
          "id" : "detekt.complexity.ReplaceSafeCallChainWithRun",
          "name" : "ReplaceSafeCallChainWithRun",
          "shortDescription" : {
            "text" : "Chains of safe calls on non-nullable types can be surrounded with run {}"
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#replacesafecallchainwithrun"
        }, {
          "id" : "detekt.empty-blocks.EmptyWhenBlock",
          "name" : "EmptyWhenBlock",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptywhenblock"
        }, {
          "id" : "detekt.naming.FunctionNaming",
          "name" : "FunctionNaming",
          "shortDescription" : {
            "text" : "Function names should follow the naming convention set in the configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#functionnaming"
        }, {
          "id" : "detekt.style.UseCheckOrError",
          "name" : "UseCheckOrError",
          "shortDescription" : {
            "text" : "Use check() or error() instead of throwing an IllegalStateException."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#usecheckorerror"
        }, {
          "id" : "detekt.style.SpacingBetweenPackageAndImports",
          "name" : "SpacingBetweenPackageAndImports",
          "shortDescription" : {
            "text" : "Violation of the package declaration style."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#spacingbetweenpackageandimports"
        }, {
          "id" : "detekt.style.MagicNumber",
          "name" : "MagicNumber",
          "shortDescription" : {
            "text" : "Report magic numbers. Magic number is a numeric literal that is not defined as a constant and hence it's unclear what the purpose of this number is. It's better to declare such numbers as constants and give them a proper name. By default, -1, 0, 1, and 2 are not considered to be magic numbers."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#magicnumber"
        }, {
          "id" : "detekt.exceptions.ThrowingExceptionInMain",
          "name" : "ThrowingExceptionInMain",
          "shortDescription" : {
            "text" : "The main method should not throw an exception."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#throwingexceptioninmain"
        }, {
          "id" : "detekt.style.UseRequire",
          "name" : "UseRequire",
          "shortDescription" : {
            "text" : "Use require() instead of throwing an IllegalArgumentException."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#userequire"
        }, {
          "id" : "detekt.comments.AbsentOrWrongFileLicense",
          "name" : "AbsentOrWrongFileLicense",
          "shortDescription" : {
            "text" : "License text is absent or incorrect in the file."
          },
          "helpUri" : "https://detekt.github.io/detekt/comments.html#absentorwrongfilelicense"
        }, {
          "id" : "detekt.naming.MemberNameEqualsClassName",
          "name" : "MemberNameEqualsClassName",
          "shortDescription" : {
            "text" : "A member should not be given the same name as its parent class or object."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#membernameequalsclassname"
        }, {
          "id" : "detekt.style.ThrowsCount",
          "name" : "ThrowsCount",
          "shortDescription" : {
            "text" : "Restrict the number of throw statements in methods."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#throwscount"
        }, {
          "id" : "detekt.empty-blocks.EmptyDoWhileBlock",
          "name" : "EmptyDoWhileBlock",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptydowhileblock"
        }, {
          "id" : "detekt.style.MandatoryBracesLoops",
          "name" : "MandatoryBracesLoops",
          "shortDescription" : {
            "text" : "Multi-line loop was found that does not have braces. These should be added to improve readability."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#mandatorybracesloops"
        }, {
          "id" : "detekt.complexity.LongParameterList",
          "name" : "LongParameterList",
          "shortDescription" : {
            "text" : "The more parameters a function has the more complex it is. Long parameter lists are often used to control complex algorithms and violate the Single Responsibility Principle. Prefer functions with short parameter lists."
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#longparameterlist"
        }, {
          "id" : "detekt.empty-blocks.EmptyTryBlock",
          "name" : "EmptyTryBlock",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptytryblock"
        }, {
          "id" : "detekt.naming.FunctionParameterNaming",
          "name" : "FunctionParameterNaming",
          "shortDescription" : {
            "text" : "Function parameter names should follow the naming convention set in the projects configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#functionparameternaming"
        }, {
          "id" : "detekt.comments.CommentOverPrivateProperty",
          "name" : "CommentOverPrivateProperty",
          "shortDescription" : {
            "text" : "Private properties should be named such that they explain themselves even without a comment."
          },
          "helpUri" : "https://detekt.github.io/detekt/comments.html#commentoverprivateproperty"
        }, {
          "id" : "detekt.potential-bugs.IteratorNotThrowingNoSuchElementException",
          "name" : "IteratorNotThrowingNoSuchElementException",
          "shortDescription" : {
            "text" : "The next() method of an Iterator implementation should throw a NoSuchElementException when there are no more elements to return"
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#iteratornotthrowingnosuchelementexception"
        }, {
          "id" : "detekt.potential-bugs.WrongEqualsTypeParameter",
          "name" : "WrongEqualsTypeParameter",
          "shortDescription" : {
            "text" : "Wrong parameter type for equals() method found. To correctly override the equals() method use Any?"
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#wrongequalstypeparameter"
        }, {
          "id" : "detekt.style.WildcardImport",
          "name" : "WildcardImport",
          "shortDescription" : {
            "text" : "Wildcard imports should be replaced with imports using fully qualified class names. Wildcard imports can lead to naming conflicts. A library update can introduce naming clashes with your classes which results in compilation errors."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#wildcardimport"
        }, {
          "id" : "detekt.empty-blocks.EmptyElseBlock",
          "name" : "EmptyElseBlock",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptyelseblock"
        }, {
          "id" : "detekt.style.MaxLineLength",
          "name" : "MaxLineLength",
          "shortDescription" : {
            "text" : "Line detected that is longer than the defined maximum line length in the code style."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#maxlinelength"
        }, {
          "id" : "detekt.potential-bugs.LateinitUsage",
          "name" : "LateinitUsage",
          "shortDescription" : {
            "text" : "Usage of lateinit detected. Using lateinit for property initialization is error prone, try using constructor injection or delegation."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#lateinitusage"
        }, {
          "id" : "detekt.potential-bugs.MapGetWithNotNullAssertionOperator",
          "name" : "MapGetWithNotNullAssertionOperator",
          "shortDescription" : {
            "text" : "map.get() with not-null assertion operator (!!) can result in a NullPointerException. Consider usage of map.getValue(), map.getOrDefault() or map.getOrElse() instead."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#mapgetwithnotnullassertionoperator"
        }, {
          "id" : "detekt.style.VarCouldBeVal",
          "name" : "VarCouldBeVal",
          "shortDescription" : {
            "text" : "Var declaration could be val."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#varcouldbeval"
        }, {
          "id" : "detekt.style.LibraryCodeMustSpecifyReturnType",
          "name" : "LibraryCodeMustSpecifyReturnType",
          "shortDescription" : {
            "text" : "Library functions/properties should have an explicit return type. Inferred return type can easily be changed by mistake which may lead to breaking changes."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#librarycodemustspecifyreturntype"
        }, {
          "id" : "detekt.complexity.StringLiteralDuplication",
          "name" : "StringLiteralDuplication",
          "shortDescription" : {
            "text" : "Multiple occurrences of the same string literal within a single file detected."
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#stringliteralduplication"
        }, {
          "id" : "detekt.performance.ArrayPrimitive",
          "name" : "ArrayPrimitive",
          "shortDescription" : {
            "text" : "Using Array<Primitive> leads to implicit boxing and a performance hit"
          },
          "helpUri" : "https://detekt.github.io/detekt/performance.html#arrayprimitive"
        }, {
          "id" : "detekt.performance.ForEachOnRange",
          "name" : "ForEachOnRange",
          "shortDescription" : {
            "text" : "Using the forEach method on ranges has a heavy performance cost. Prefer using simple for loops."
          },
          "helpUri" : "https://detekt.github.io/detekt/performance.html#foreachonrange"
        }, {
          "id" : "detekt.style.NestedClassesVisibility",
          "name" : "NestedClassesVisibility",
          "shortDescription" : {
            "text" : "The explicit public modifier still results in an internal nested class."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#nestedclassesvisibility"
        }, {
          "id" : "detekt.style.ForbiddenImport",
          "name" : "ForbiddenImport",
          "shortDescription" : {
            "text" : "Mark forbidden imports. A forbidden import could be an import for an unstable / experimental apiand hence you might want to mark it as forbidden in order to get warned about the usage."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#forbiddenimport"
        }, {
          "id" : "detekt.complexity.LongMethod",
          "name" : "LongMethod",
          "shortDescription" : {
            "text" : "One method should have one responsibility. Long methods tend to handle many things at once. Prefer smaller methods to make them easier to understand."
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#longmethod"
        }, {
          "id" : "detekt.exceptions.ReturnFromFinally",
          "name" : "ReturnFromFinally",
          "shortDescription" : {
            "text" : "Do not return within a finally statement. This can discard exceptions."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#returnfromfinally"
        }, {
          "id" : "detekt.complexity.ComplexInterface",
          "name" : "ComplexInterface",
          "shortDescription" : {
            "text" : "An interface contains too many functions and properties. Large classes tend to handle many things at once. An interface should have one responsibility. Split up large interfaces into smaller ones that are easier to understand."
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#complexinterface"
        }, {
          "id" : "detekt.exceptions.ThrowingExceptionFromFinally",
          "name" : "ThrowingExceptionFromFinally",
          "shortDescription" : {
            "text" : "Do not throw an exception within a finally statement. This can discard exceptions and is confusing."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#throwingexceptionfromfinally"
        }, {
          "id" : "detekt.empty-blocks.EmptyCatchBlock",
          "name" : "EmptyCatchBlock",
          "shortDescription" : {
            "text" : "Empty catch block detected. Empty catch blocks indicate that an exception is ignored and not handled."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptycatchblock"
        }, {
          "id" : "detekt.style.NoTabs",
          "name" : "NoTabs",
          "shortDescription" : {
            "text" : "Checks if tabs are used in Kotlin files."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#notabs"
        }, {
          "id" : "detekt.style.ForbiddenMethodCall",
          "name" : "ForbiddenMethodCall",
          "shortDescription" : {
            "text" : "Mark forbidden methods. A forbidden method could be an invocation of an unstable / experimental method and hence you might want to mark it as forbidden in order to get warned about the usage."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#forbiddenmethodcall"
        }, {
          "id" : "detekt.comments.UndocumentedPublicFunction",
          "name" : "UndocumentedPublicFunction",
          "shortDescription" : {
            "text" : "Public functions require documentation."
          },
          "helpUri" : "https://detekt.github.io/detekt/comments.html#undocumentedpublicfunction"
        }, {
          "id" : "detekt.exceptions.TooGenericExceptionThrown",
          "name" : "TooGenericExceptionThrown",
          "shortDescription" : {
            "text" : "Thrown exception is too generic. Prefer throwing project specific exceptions to handle error cases."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#toogenericexceptionthrown"
        }, {
          "id" : "detekt.empty-blocks.EmptyInitBlock",
          "name" : "EmptyInitBlock",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptyinitblock"
        }, {
          "id" : "detekt.exceptions.PrintStackTrace",
          "name" : "PrintStackTrace",
          "shortDescription" : {
            "text" : "Do not print an stack trace. These debug statements should be replaced with a logger or removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#printstacktrace"
        }, {
          "id" : "detekt.exceptions.RethrowCaughtException",
          "name" : "RethrowCaughtException",
          "shortDescription" : {
            "text" : "Do not rethrow a caught exception of the same type."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#rethrowcaughtexception"
        }, {
          "id" : "detekt.style.UseDataClass",
          "name" : "UseDataClass",
          "shortDescription" : {
            "text" : "Classes that do nothing but hold data should be replaced with a data class."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#usedataclass"
        }, {
          "id" : "detekt.complexity.ComplexCondition",
          "name" : "ComplexCondition",
          "shortDescription" : {
            "text" : "Complex conditions should be simplified and extracted into well-named methods if necessary."
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#complexcondition"
        }, {
          "id" : "detekt.style.UseRequireNotNull",
          "name" : "UseRequireNotNull",
          "shortDescription" : {
            "text" : "Use requireNotNull() instead of require() for checking not-null."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#userequirenotnull"
        }, {
          "id" : "detekt.naming.FunctionMinLength",
          "name" : "FunctionMinLength",
          "shortDescription" : {
            "text" : "Function names should not be shorter than the minimum defined in the configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#functionminlength"
        }, {
          "id" : "detekt.exceptions.SwallowedException",
          "name" : "SwallowedException",
          "shortDescription" : {
            "text" : "The caught exception is swallowed. The original exception could be lost."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#swallowedexception"
        }, {
          "id" : "detekt.naming.MatchingDeclarationName",
          "name" : "MatchingDeclarationName",
          "shortDescription" : {
            "text" : "If a source file contains only a single non-private top-level class or object, the file name should reflect the case-sensitive name plus the .kt extension."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#matchingdeclarationname"
        }, {
          "id" : "detekt.naming.NonBooleanPropertyPrefixedWithIs",
          "name" : "NonBooleanPropertyPrefixedWithIs",
          "shortDescription" : {
            "text" : "Only boolean property names can start with 'is' prefix."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#nonbooleanpropertyprefixedwithis"
        }, {
          "id" : "detekt.style.LibraryEntitiesShouldNotBePublic",
          "name" : "LibraryEntitiesShouldNotBePublic",
          "shortDescription" : {
            "text" : "Library class should not be public"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#libraryentitiesshouldnotbepublic"
        }, {
          "id" : "detekt.style.FunctionOnlyReturningConstant",
          "name" : "FunctionOnlyReturningConstant",
          "shortDescription" : {
            "text" : "A function that only returns a constant is misleading. Consider declaring a constant instead"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#functiononlyreturningconstant"
        }, {
          "id" : "detekt.style.UnderscoresInNumericLiterals",
          "name" : "UnderscoresInNumericLiterals",
          "shortDescription" : {
            "text" : "Report missing or invalid underscores in decimal base 10 numeric literals. Numeric literals should be underscore separated to increase readability. Underscores that do not make groups of 3 digits are also reported."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#underscoresinnumericliterals"
        }, {
          "id" : "detekt.style.UseIfEmptyOrIfBlank",
          "name" : "UseIfEmptyOrIfBlank",
          "shortDescription" : {
            "text" : "Use 'ifEmpty' or 'ifBlank' instead of 'isEmpty' or 'isBlank' to assign default value."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#useifemptyorifblank"
        }, {
          "id" : "detekt.style.CollapsibleIfStatements",
          "name" : "CollapsibleIfStatements",
          "shortDescription" : {
            "text" : "Two if statements which could be collapsed were detected. These statements can be merged to improve readability."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#collapsibleifstatements"
        }, {
          "id" : "detekt.comments.UndocumentedPublicClass",
          "name" : "UndocumentedPublicClass",
          "shortDescription" : {
            "text" : "Public classes, interfaces and objects require documentation."
          },
          "helpUri" : "https://detekt.github.io/detekt/comments.html#undocumentedpublicclass"
        }, {
          "id" : "detekt.complexity.NestedBlockDepth",
          "name" : "NestedBlockDepth",
          "shortDescription" : {
            "text" : "Excessive nesting leads to hidden complexity. Prefer extracting code to make it easier to understand."
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#nestedblockdepth"
        }, {
          "id" : "detekt.style.UnnecessaryAnnotationUseSiteTarget",
          "name" : "UnnecessaryAnnotationUseSiteTarget",
          "shortDescription" : {
            "text" : "Unnecessary Annotation use-site Target. It can be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#unnecessaryannotationusesitetarget"
        }, {
          "id" : "detekt.complexity.LargeClass",
          "name" : "LargeClass",
          "shortDescription" : {
            "text" : "One class should have one responsibility. Large classes tend to handle many things at once. Split up large classes into smaller classes that are easier to understand."
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#largeclass"
        }, {
          "id" : "detekt.potential-bugs.UnconditionalJumpStatementInLoop",
          "name" : "UnconditionalJumpStatementInLoop",
          "shortDescription" : {
            "text" : "An unconditional jump statement in a loop is useless. The loop itself is only executed once."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#unconditionaljumpstatementinloop"
        }, {
          "id" : "detekt.complexity.TooManyFunctions",
          "name" : "TooManyFunctions",
          "shortDescription" : {
            "text" : "Too many functions inside a/an file/class/object/interface always indicate a violation of the single responsibility principle. Maybe the file/class/object/interface wants to manage too many things at once. Extract functionality which clearly belongs together."
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#toomanyfunctions"
        }, {
          "id" : "detekt.coroutines.RedundantSuspendModifier",
          "name" : "RedundantSuspendModifier",
          "shortDescription" : {
            "text" : "`suspend` modifier is only needed for functions that contain suspending calls"
          },
          "helpUri" : "https://detekt.github.io/detekt/coroutines.html#redundantsuspendmodifier"
        }, {
          "id" : "detekt.comments.EndOfSentenceFormat",
          "name" : "EndOfSentenceFormat",
          "shortDescription" : {
            "text" : "The first sentence in a KDoc comment should end with correct punctuation."
          },
          "helpUri" : "https://detekt.github.io/detekt/comments.html#endofsentenceformat"
        }, {
          "id" : "detekt.style.UseIfInsteadOfWhen",
          "name" : "UseIfInsteadOfWhen",
          "shortDescription" : {
            "text" : "Binary expressions are better expressed using an 'if' expression than a 'when' expression."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#useifinsteadofwhen"
        }, {
          "id" : "detekt.naming.VariableNaming",
          "name" : "VariableNaming",
          "shortDescription" : {
            "text" : "Variable names should follow the naming convention set in the projects configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#variablenaming"
        }, {
          "id" : "detekt.style.DataClassContainsFunctions",
          "name" : "DataClassContainsFunctions",
          "shortDescription" : {
            "text" : "Data classes should mainly be used to store data and should not have any extra functions. (Compiler will automatically generate equals, toString and hashCode functions)"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#dataclasscontainsfunctions"
        }, {
          "id" : "detekt.style.ExplicitItLambdaParameter",
          "name" : "ExplicitItLambdaParameter",
          "shortDescription" : {
            "text" : "Declaring lambda parameters as `it` is redundant."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#explicititlambdaparameter"
        }, {
          "id" : "detekt.style.UnusedImports",
          "name" : "UnusedImports",
          "shortDescription" : {
            "text" : "Unused Imports are dead code and should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#unusedimports"
        }, {
          "id" : "detekt.potential-bugs.UnsafeCast",
          "name" : "UnsafeCast",
          "shortDescription" : {
            "text" : "Cast operator throws an exception if the cast is not possible."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#unsafecast"
        }, {
          "id" : "detekt.naming.EnumNaming",
          "name" : "EnumNaming",
          "shortDescription" : {
            "text" : "Enum names should follow the naming convention set in the projects configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#enumnaming"
        }, {
          "id" : "detekt.style.ExplicitCollectionElementAccessMethod",
          "name" : "ExplicitCollectionElementAccessMethod",
          "shortDescription" : {
            "text" : "Prefer usage of indexed access operator [] for map element access or insert methods"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#explicitcollectionelementaccessmethod"
        }, {
          "id" : "detekt.naming.TopLevelPropertyNaming",
          "name" : "TopLevelPropertyNaming",
          "shortDescription" : {
            "text" : "Top level property names should follow the naming convention set in the projects configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#toplevelpropertynaming"
        }, {
          "id" : "detekt.style.MandatoryBracesIfStatements",
          "name" : "MandatoryBracesIfStatements",
          "shortDescription" : {
            "text" : "Multi-line if statement was found that does not have braces. These should be added to improve readability."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#mandatorybracesifstatements"
        }, {
          "id" : "detekt.exceptions.TooGenericExceptionCaught",
          "name" : "TooGenericExceptionCaught",
          "shortDescription" : {
            "text" : "Caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled."
          },
          "helpUri" : "https://detekt.github.io/detekt/exceptions.html#toogenericexceptioncaught"
        }, {
          "id" : "detekt.style.MayBeConst",
          "name" : "MayBeConst",
          "shortDescription" : {
            "text" : "Reports vals that can be const val instead."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#maybeconst"
        }, {
          "id" : "detekt.complexity.NamedArguments",
          "name" : "NamedArguments",
          "shortDescription" : {
            "text" : "Function invocation with more than 3 parameters must all be named"
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#namedarguments"
        }, {
          "id" : "detekt.complexity.LabeledExpression",
          "name" : "LabeledExpression",
          "shortDescription" : {
            "text" : "Expression with labels increase complexity and affect maintainability."
          },
          "helpUri" : "https://detekt.github.io/detekt/complexity.html#labeledexpression"
        }, {
          "id" : "detekt.style.ReturnCount",
          "name" : "ReturnCount",
          "shortDescription" : {
            "text" : "Restrict the number of return statements in methods."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#returncount"
        }, {
          "id" : "detekt.style.RedundantHigherOrderMapUsage",
          "name" : "RedundantHigherOrderMapUsage",
          "shortDescription" : {
            "text" : "Checks for Redundant 'map' calls."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#redundanthigherordermapusage"
        }, {
          "id" : "detekt.empty-blocks.EmptyIfBlock",
          "name" : "EmptyIfBlock",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptyifblock"
        }, {
          "id" : "detekt.potential-bugs.InvalidRange",
          "name" : "InvalidRange",
          "shortDescription" : {
            "text" : "If a for loops condition is false before the first iteration, the loop will never get executed."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#invalidrange"
        }, {
          "id" : "detekt.coroutines.SuspendFunWithFlowReturnType",
          "name" : "SuspendFunWithFlowReturnType",
          "shortDescription" : {
            "text" : "`suspend` modifier should not be used for functions that return a Coroutines Flow type. Flows are cold streams and invoking a function that returns one should not produce any side effects."
          },
          "helpUri" : "https://detekt.github.io/detekt/coroutines.html#suspendfunwithflowreturntype"
        }, {
          "id" : "detekt.style.UnusedPrivateMember",
          "name" : "UnusedPrivateMember",
          "shortDescription" : {
            "text" : "Private member is unused."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#unusedprivatemember"
        }, {
          "id" : "detekt.potential-bugs.EqualsAlwaysReturnsTrueOrFalse",
          "name" : "EqualsAlwaysReturnsTrueOrFalse",
          "shortDescription" : {
            "text" : "Having an equals method which always returns true or false is not a good idea. It does not follow the contract of this method. Consider a good default implementation. For example this == other"
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#equalsalwaysreturnstrueorfalse"
        }, {
          "id" : "detekt.style.UseCheckNotNull",
          "name" : "UseCheckNotNull",
          "shortDescription" : {
            "text" : "Use checkNotNull() instead of check() for checking not-null."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#usechecknotnull"
        }, {
          "id" : "detekt.potential-bugs.EqualsWithHashCodeExist",
          "name" : "EqualsWithHashCodeExist",
          "shortDescription" : {
            "text" : "Always override hashCode when you override equals. All hash-based collections depend on objects meeting the equals-contract. Two equal objects must produce the same hashcode. When inheriting equals or hashcode, override the inherited and call the super method for clarification."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#equalswithhashcodeexist"
        }, {
          "id" : "detekt.naming.ObjectPropertyNaming",
          "name" : "ObjectPropertyNaming",
          "shortDescription" : {
            "text" : "Property names inside objects should follow the naming convention set in the projects configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#objectpropertynaming"
        }, {
          "id" : "detekt.style.EqualsNullCall",
          "name" : "EqualsNullCall",
          "shortDescription" : {
            "text" : "Equals() method is called with null as parameter. Consider using == to compare to null."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#equalsnullcall"
        }, {
          "id" : "detekt.naming.ForbiddenClassName",
          "name" : "ForbiddenClassName",
          "shortDescription" : {
            "text" : "Forbidden class name as per configuration detected."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#forbiddenclassname"
        }, {
          "id" : "detekt.style.ModifierOrder",
          "name" : "ModifierOrder",
          "shortDescription" : {
            "text" : "Modifiers are not in the correct order."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#modifierorder"
        }, {
          "id" : "detekt.potential-bugs.IgnoredReturnValue",
          "name" : "IgnoredReturnValue",
          "shortDescription" : {
            "text" : "This call returns a value which is ignored"
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#ignoredreturnvalue"
        }, {
          "id" : "detekt.style.SerialVersionUIDInSerializableClass",
          "name" : "SerialVersionUIDInSerializableClass",
          "shortDescription" : {
            "text" : "A class which implements the Serializable interface does not define a correct serialVersionUID field. The serialVersionUID field should be a constant long value inside a companion object."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#serialversionuidinserializableclass"
        }, {
          "id" : "detekt.potential-bugs.MissingWhenCase",
          "name" : "MissingWhenCase",
          "shortDescription" : {
            "text" : "Check usage of `when` used as a statement and don't compare all enum or sealed class cases."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#missingwhencase"
        }, {
          "id" : "detekt.style.RedundantExplicitType",
          "name" : "RedundantExplicitType",
          "shortDescription" : {
            "text" : "Type does not need to be stated explicitly and can be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#redundantexplicittype"
        }, {
          "id" : "detekt.naming.VariableMinLength",
          "name" : "VariableMinLength",
          "shortDescription" : {
            "text" : "Variable names should not be shorter than the minimum defined in the configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#variableminlength"
        }, {
          "id" : "detekt.empty-blocks.EmptyForBlock",
          "name" : "EmptyForBlock",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptyforblock"
        }, {
          "id" : "detekt.potential-bugs.DuplicateCaseInWhenExpression",
          "name" : "DuplicateCaseInWhenExpression",
          "shortDescription" : {
            "text" : "Duplicated case statements in when expression. Both cases should be merged."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#duplicatecaseinwhenexpression"
        }, {
          "id" : "detekt.potential-bugs.UnsafeCallOnNullableType",
          "name" : "UnsafeCallOnNullableType",
          "shortDescription" : {
            "text" : "It will throw a NullPointerException at runtime if your nullable value is null."
          },
          "helpUri" : "https://detekt.github.io/detekt/potential-bugs.html#unsafecallonnullabletype"
        }, {
          "id" : "detekt.style.UnusedPrivateClass",
          "name" : "UnusedPrivateClass",
          "shortDescription" : {
            "text" : "Private class is unused."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#unusedprivateclass"
        }, {
          "id" : "detekt.style.UseEmptyCounterpart",
          "name" : "UseEmptyCounterpart",
          "shortDescription" : {
            "text" : "Instantiation of an object's \"empty\" state should use the object's \"empty\" initializer"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#useemptycounterpart"
        }, {
          "id" : "detekt.style.UselessCallOnNotNull",
          "name" : "UselessCallOnNotNull",
          "shortDescription" : {
            "text" : "This call on non-null reference may be reduced or removed. Some calls are intended to be called on nullable collection or text types (e.g. String?). When this call is used on a reference to a non-null type (e.g. String) it is redundant and will have no effect, so it can be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#uselesscallonnotnull"
        }, {
          "id" : "detekt.style.UnnecessaryLet",
          "name" : "UnnecessaryLet",
          "shortDescription" : {
            "text" : "The `let` usage is unnecessary"
          },
          "helpUri" : "https://detekt.github.io/detekt/style.html#unnecessarylet"
        }, {
          "id" : "detekt.empty-blocks.EmptyDefaultConstructor",
          "name" : "EmptyDefaultConstructor",
          "shortDescription" : {
            "text" : "Empty block of code detected. As they serve no purpose they should be removed."
          },
          "helpUri" : "https://detekt.github.io/detekt/empty-blocks.html#emptydefaultconstructor"
        }, {
          "id" : "detekt.naming.ConstructorParameterNaming",
          "name" : "ConstructorParameterNaming",
          "shortDescription" : {
            "text" : "Constructor parameter names should follow the naming convention set in the projects configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#constructorparameternaming"
        }, {
          "id" : "detekt.naming.ClassNaming",
          "name" : "ClassNaming",
          "shortDescription" : {
            "text" : "A class or object's name should fit the naming pattern defined in the projects configuration."
          },
          "helpUri" : "https://detekt.github.io/detekt/naming.html#classnaming"
        }, {
          "id" : "detekt.performance.UnnecessaryTemporaryInstantiation",
          "name" : "UnnecessaryTemporaryInstantiation",
          "shortDescription" : {
            "text" : "Avoid temporary objects when converting primitive types to String."
          },
          "helpUri" : "https://detekt.github.io/detekt/performance.html#unnecessarytemporaryinstantiation"
        } ],
        "language" : "en"
      }
    },
    "results" : [ {
      "ruleId" : "detekt.complexity.TooManyFunctions",
      "message" : {
        "text" : "File '/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/Collections.kt' with '11' functions detected. Defined threshold inside files is set to '11'"
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/Collections.kt"
          },
          "region" : {
            "startLine" : 1,
            "startColumn" : 1
          }
        }
      } ]
    }, {
      "ruleId" : "detekt.complexity.TooManyFunctions",
      "message" : {
        "text" : "File '/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/Dates.kt' with '13' functions detected. Defined threshold inside files is set to '11'"
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/Dates.kt"
          },
          "region" : {
            "startLine" : 1,
            "startColumn" : 1
          }
        }
      } ]
    }, {
      "ruleId" : "detekt.exceptions.TooGenericExceptionCaught",
      "message" : {
        "text" : "Caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled."
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/EventBus.kt"
          },
          "region" : {
            "startLine" : 101,
            "startColumn" : 22
          }
        }
      } ]
    }, {
      "ruleId" : "detekt.naming.MemberNameEqualsClassName",
      "message" : {
        "text" : "A member is named after the class. This might result in confusion. Either rename the member or change it to a constructor."
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/test/kotlin/io/gitlab/arturbosch/kutils/DelegatesTest.kt"
          },
          "region" : {
            "startLine" : 13,
            "startColumn" : 9
          }
        }
      } ]
    }, {
      "ruleId" : "detekt.naming.FunctionNaming",
      "message" : {
        "text" : "Function names should match the pattern: ([a-z][a-zA-Z0-9]*)|(`.*`)"
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/Paths.kt"
          },
          "region" : {
            "startLine" : 46,
            "startColumn" : 12
          }
        }
      } ]
    }, {
      "ruleId" : "detekt.naming.FunctionNaming",
      "message" : {
        "text" : "Function names should match the pattern: ([a-z][a-zA-Z0-9]*)|(`.*`)"
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/ConsoleTrees.kt"
          },
          "region" : {
            "startLine" : 65,
            "startColumn" : 9
          }
        }
      } ]
    }, {
      "ruleId" : "detekt.performance.SpreadOperator",
      "message" : {
        "text" : "In most cases using a spread operator causes a full copy of the array to be created before calling a method. This may result in a performance penalty."
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/Async.kt"
          },
          "region" : {
            "startLine" : 42,
            "startColumn" : 28
          }
        }
      } ]
    }, {
      "ruleId" : "detekt.style.MagicNumber",
      "message" : {
        "text" : "This expression contains a magic number. Consider defining it to a well named constant."
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/args/ArgParser.kt"
          },
          "region" : {
            "startLine" : 16,
            "startColumn" : 54
          }
        }
      } ]
    }, {
      "ruleId" : "detekt.style.MagicNumber",
      "message" : {
        "text" : "This expression contains a magic number. Consider defining it to a well named constant."
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/args/ArgParser.kt"
          },
          "region" : {
            "startLine" : 17,
            "startColumn" : 55
          }
        }
      } ]
    }, {
      "ruleId" : "detekt.style.MagicNumber",
      "message" : {
        "text" : "This expression contains a magic number. Consider defining it to a well named constant."
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/args/ArgParser.kt"
          },
          "region" : {
            "startLine" : 18,
            "startColumn" : 53
          }
        }
      } ]
    }, {
      "ruleId" : "detekt.style.MagicNumber",
      "message" : {
        "text" : "This expression contains a magic number. Consider defining it to a well named constant."
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/args/ArgParser.kt"
          },
          "region" : {
            "startLine" : 133,
            "startColumn" : 26
          }
        }
      } ]
    }, {
      "ruleId" : "detekt.style.MagicNumber",
      "message" : {
        "text" : "This expression contains a magic number. Consider defining it to a well named constant."
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/args/Options.kt"
          },
          "region" : {
            "startLine" : 41,
            "startColumn" : 28
          }
        }
      } ]
    }, {
      "ruleId" : "detekt.style.MagicNumber",
      "message" : {
        "text" : "This expression contains a magic number. Consider defining it to a well named constant."
      },
      "locations" : [ {
        "physicalLocation" : {
          "artifactLocation" : {
            "uri" : "/home/artur/git/repos/kutils/src/main/kotlin/io/gitlab/arturbosch/kutils/args/Options.kt"
          },
          "region" : {
            "startLine" : 41,
            "startColumn" : 50
          }
        }
      } ]
    } ]
  } ]
}
ghost commented 3 years ago

Hi Artur, this looks really good!

In the online validator, you can check "Additional suggestions" to see more ways you can make your SARIF more useful, and also to conform to certain conventions we recommend.

josepalafox commented 3 years ago

Hey I just wanted to check in and see if we could support getting this merged. Please let us know if any help is needed!

BraisGabin commented 3 years ago

1.15.0-RC1 supports sarif. It will be great if you can test it in some use cases and give feedback.

ghost commented 3 years ago

@michaelcfanning @eddynaka (handling this going forwards, AFAIK) -- FYI

josepalafox commented 3 years ago

OK I'll see if someone on our team can review this and take a stab at writing a workflow file and an action to upload the SARIF to GitHub.

On Thu, Dec 10, 2020 at 10:37 AM Larry Golding notifications@github.com wrote:

@michaelcfanning https://github.com/michaelcfanning @eddynaka https://github.com/eddynaka (handling this going forwards, AFAIK) -- FYI

β€” You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/detekt/detekt/issues/3045#issuecomment-742713617, or unsubscribe https://github.com/notifications/unsubscribe-auth/AALPN2DYM5ZNY6BGHPJFQ6LSUEINHANCNFSM4QRMLQVQ .

-- Jose Palafox Strategic Accounts @ GitHub JosePalafox@GitHub.com 503.877.2403

swinton commented 3 years ago

1.15.0-RC1 supports sarif. It will be great if you can test it in some use cases and give feedback.

Trying to test this over here.

Could someone provide some directions on how to generate the SARIF report? I tried the following after building the .jar from https://github.com/detekt/sarif4j, but no luck, I'm only getting the plain text report πŸ€”

detekt --plugins detekt-report-sarif.jar --report sarif:reports/detekt.sarif
arturbosch commented 3 years ago

Hi @swinton,

./detekt-cli/build/run/detekt -i ~/git/repos/kutils/ -p detekt-report-sarif/build/libs/detekt-report-sarif-1.15.0-RC1.jar -r sarif:sarif.json works fine for me for relative paths and -r sarif:/home/artur/sarif.json for absolute ones.

You mentioned the jar from https://github.com/detekt/sarif4j . sarif4j is bundled inside detekt-report-sarif so you have to referene this jar with the --plugins flag.

Hope it helps. I will test it asap with the published jar and edit my answer.

Edit:

Please try

  1. Download https://github.com/detekt/detekt/releases/tag/v1.15.0-RC1 (detekt-all.jar)
  2. Download https://mvnrepository.com/artifact/io.gitlab.arturbosch.detekt/detekt-report-sarif/1.15.0-RC1
  3. Edit paths and run: java -jar Downloads/detekt-cli-1.15.0-RC1-all.jar --plugins Downloads/detekt-report-sarif-1.15.0-RC1.jar --input ~/git/repos/kutils/src/ --report sarif:/home/artur/test.sarif.json
swinton commented 3 years ago

Thanks so much for the pointers, @arturbosch, I was able to generate a SARIF report:

https://gist.github.com/swinton/43abb47ab719e7c3d67f789115defcff

JFYI here's how the report looks once loaded into GitHub:

We're not able to include the preview since (I believe) the results use absolute paths instead of relative paths, but looks like relative path support may be on the way?

chao2zhang commented 3 years ago

@swinton Thank you for testing and reporting this. @arturbosch I can continue your work if you do not have free cycles.

arturbosch commented 3 years ago

Sure, if you have some spare time in the next days you can take a look at https://github.com/detekt/detekt/pull/2492/. I think the PR is a good starting point. Feel free to @mention me in a draft.

chao2zhang commented 3 years ago

@swinton According to Github Docs, I did not find any official documentation of uploading multiple .sarif files. Is it officially recommended that we merge all .sarif files into one?

Update: Never mind. I found https://github.com/github/codeql-action/issues/220 where it currently supports a single file or the files under a directory