From 8e14c94e8b3f0ab8deecc3738a1b66fd028306c6 Mon Sep 17 00:00:00 2001 From: Tor Norbye Date: Wed, 3 Jul 2024 07:18:32 -0700 Subject: [PATCH] Fix some typos and grammar --- docs/README.md.html | 1 + docs/api-guide.html | 55 ++++++++++++++--------------- docs/api-guide/ast-analysis.md.html | 55 ++++++++++++++--------------- docs/changes.md.html | 2 ++ docs/index.html | 4 ++- 5 files changed, 60 insertions(+), 57 deletions(-) diff --git a/docs/README.md.html b/docs/README.md.html index dd5a4108..3135b246 100644 --- a/docs/README.md.html +++ b/docs/README.md.html @@ -36,6 +36,7 @@ - [A Sample Lint Check](api-guide/example.md.html) - [Analyzing data flow](api-guide/dataflow-analyzer.md.html) - [Publishing a Lint check](api-guide/publishing.md.html) + - [AST Analysis](api-guide/ast-analysis.md.html) - [Unit Testing](api-guide/unit-testing.md.html) - [Test Modes](api-guide/test-modes.md.html) - [Annotations](api-guide/annotations.md.html) diff --git a/docs/api-guide.html b/docs/api-guide.html index 187f3cf7..2c55e908 100644 --- a/docs/api-guide.html +++ b/docs/api-guide.html @@ -2276,7 +2276,7 @@ Anyway, you can see there is quite a bit of detail here — tracking things like the keywords, the variables, references to for example the -package — and higher level concepts like a class and a field which I've +package — and higher level concepts like a class and a field, which I've marked with a thicker border.

@@ -2298,7 +2298,7 @@

-This is for a program which is completely equivalent to the Java one. +This program is equivalent to the Java one. But notice that it has a completely different shape! They reference different element classes, PsiClass versus KtClass, and on and on all the way down. @@ -2313,7 +2313,7 @@

-We can construct a new AST which represents the same concepts: +We can construct a new AST that represents the same concepts:

@@ -2337,7 +2337,7 @@

As you can see, the ASTs are not always identical. For Strings, in -Kotlin, we often end up with an extra parent UiInjectionHost. But for +Kotlin, we often end up with an extra parent UInjectionHost. But for our purposes, you can see that the ASTs are mostly the same, so if you handle the Kotlin scenario, you'll handle the Java ones too. @@ -2348,7 +2348,7 @@ Note that “Unified” in the name here is a bit misleading. From the name you may assume that this is some sort of superset of the ASTs across -languages — and AST that can represent everything needed by all +languages — an AST that can represent everything needed by all languages. But that's not the case! Instead, a better way to think of it is as the Java view of the AST. @@ -2362,9 +2362,8 @@ )

This is a Kotlin data class with two properties. So you might expect -that UAST would have a way to represent these concepts — properties, -and java classes. This should be a UDataClass with two UProperty -children, right? +that UAST would have a way to represent these concepts. This should +be a UDataClass with two UProperty children, right?

@@ -2442,7 +2441,7 @@

-Every node in UAST is a subclass of a UElement. There's a parent +Every node in UAST is a subclass of a UElement. There's a parent pointer, which is handy for navigating around in the AST.

@@ -2454,7 +2453,7 @@

-Or in the debugger, anytime you have a UElement, you can call +Or in the debugger, anytime you have a UElement, you can call UElement.asRecursiveLogString on it, evaluate and see what you find.

@@ -2488,7 +2487,7 @@ You generally shouldn't visit a source file on your own. Lint has a -special UElementHandler for that which is used to ensure that we don't +special UElementHandler for that, which is used to ensure we don't repeat visiting a source file thousands of times, one per detector.

@@ -2538,7 +2537,7 @@ We have our UAST tree in the top right corner. And here's the Java PSI AST behind the scenes. We can access the underlying PSI node for a -UElement by accessing the sourcePsi element. So when you do need to dip +UElement by accessing the sourcePsi property. So when you do need to dip into something language specific, that's trivial to do.

@@ -2547,9 +2546,9 @@

-Each of the UElement nodes point back into the PSI AST - whether a Java +Most UElement nodes point back to the PSI AST - whether a Java AST or a Kotlin AST. Here's the same AST, but with the type of the -sourcePsi attribute for each node added. +sourcePsi property for each node added.

@@ -2557,8 +2556,8 @@

-You can see that the class generated to represent the top level -functions here doesn't have a non-null sourcePsi, because in the +You can see that the facade class generated to contain the top level +functions has a null sourcePsi, because in the Kotlin PSI, there is no real KtClass for a facade class. And for the three members, the private field and the getter and the setter, they all correspond to the exact same, single KtProperty instance, the single @@ -2596,8 +2595,8 @@

-There are lint checks which are language specific — for example, if -you write a lint check which forbids the use of companion objects — in +There are lint checks that are language specific — for example, if +you write a lint check that forbids the use of companion objects — in that case, there's no big advantage to using UAST over PSI; it's only ever going to run on Kotlin code. (Note however that lint's APIs and convenience callbacks are all targeting UAST, so it's easier to write @@ -2622,10 +2621,10 @@

For example, let's say you need to determine if a UClass is a Kotlin -“companion object“. You could cheat and look at the class name to see if -it's ”Companion“. But that's not quite right; in Kotlin you can +“companion object”. You could cheat and look at the class name to see if +it's “Companion”. But that's not quite right; in Kotlin you can specify a custom companion object name, and of course users are free -to create classes named ”Companion“ that aren't companion objects: +to create classes named “Companion” that aren't companion objects:

class Test {
   companion object MyName { // Companion object not named "Companion"!
@@ -2635,8 +2634,8 @@
   }
 }

-The right way to do this, is using Kotlin PSI, via the -UElement.sourcePsi attribute: +The right way to do this is using Kotlin PSI, via the +UElement.sourcePsi property:

// Skip companion objects
 val source = node.sourcePsi
@@ -2645,7 +2644,7 @@
 }

(To figure out how to write the above code, use a debugger on a test -case and look at the UClass.sourcePsi attribute; you'll discover that +case and look at the UClass.sourcePsi property; you'll discover that it's some subclass of KtObjectDeclaration; look up its most general super interface or class, and then use code completion to discover available APIs, such as isCompanion().) @@ -2665,7 +2664,7 @@ Lint doesn't actually give you access to everything you need if you want to try to look up types in Kotlin PSI; you need something called the “binding context”, which is not exposed anywhere! And this omission is -deliberate, because that was an implementation detail of the old +deliberate, because this is an implementation detail of the old compiler. The future is K2; a complete rewrite of the compiler front end, which is no longer using the old binding context. And as part of the tooling support for K2, there's a new API called the “Kotlin @@ -2719,7 +2718,7 @@ Before the Kotlin lint analysis API, lint didn't have a way to reason about the Nothing type. UAST only returns Java types, which maps to -void. So instead, lint had an ugly hack which just hardcoded well known +void. So instead, lint had an ugly hack that just hardcoded well known names of methods that don't return:

if (nextStatement is UCallExpression) {
@@ -2764,7 +2763,7 @@
 
 

-Similarly on a KtDeclaration (such as a named function or property) I +Similarly, on a KtDeclaration (such as a named function or property) I can call getSymbol() to get the symbol for that method or property, to for example look up parameter information. And on a KtExpression (such as an if statement) I can call getKtType() to get the Kotlin type. @@ -2779,7 +2778,7 @@

In the new implementation of callNeverReturns, we resolve the call, -look up the corresponding function which of course is a KtSymbol +look up the corresponding function, which of course is a KtSymbol itself, and from that we get the return type, and then we can just check if it's the Nothing type. diff --git a/docs/api-guide/ast-analysis.md.html b/docs/api-guide/ast-analysis.md.html index 5cf2478d..167013d4 100644 --- a/docs/api-guide/ast-analysis.md.html +++ b/docs/api-guide/ast-analysis.md.html @@ -42,7 +42,7 @@ Anyway, you can see there is quite a bit of detail here -- tracking things like the keywords, the variables, references to for example the -package -- and higher level concepts like a class and a field which I've +package -- and higher level concepts like a class and a field, which I've marked with a thicker border. Here's the corresponding Kotlin program: @@ -60,7 +60,7 @@ ![](images/kotlin-psi.png) -This is for a program which is completely equivalent to the Java one. +This program is equivalent to the Java one. But notice that it has a completely different shape! They reference different element classes, `PsiClass` versus `KtClass`, and on and on all the way down. @@ -70,7 +70,7 @@ ## UAST -We can construct a new AST which represents the same concepts: +We can construct a new AST that represents the same concepts: ![](images/uast-java.png) @@ -84,7 +84,7 @@ ![](images/uast-kotlin.png) As you can see, the ASTs are not always identical. For Strings, in -Kotlin, we often end up with an extra parent `UiInjectionHost`. But for +Kotlin, we often end up with an extra parent `UInjectionHost`. But for our purposes, you can see that the ASTs are mostly the same, so if you handle the Kotlin scenario, you'll handle the Java ones too. @@ -92,7 +92,7 @@ Note that “Unified” in the name here is a bit misleading. From the name you may assume that this is some sort of superset of the ASTs across -languages -- and AST that can represent everything needed by all +languages -- an AST that can represent everything needed by all languages. But that's not the case! Instead, a better way to think of it is as the **Java view** of the AST. @@ -106,9 +106,8 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is a Kotlin data class with two properties. So you might expect -that UAST would have a way to represent these concepts -- properties, -and java classes. This should be a `UDataClass` with two `UProperty` -children, right? +that UAST would have a way to represent these concepts. This should +be a `UDataClass` with two `UProperty` children, right? But Java doesn't support properties. If you try to access a `Person` instance from Java, you'll notice that it exposes a number of public @@ -166,7 +165,7 @@ ## UElement -Every node in UAST is a subclass of a UElement. There's a parent +Every node in UAST is a subclass of a `UElement`. There's a parent pointer, which is handy for navigating around in the AST. The real skill you need for writing lint checks is understanding the @@ -174,7 +173,7 @@ is to create the Kotlin or Java code you want, in a unit test, and then in your detector, recursively print out the UAST as a tree. -Or in the debugger, anytime you have a UElement, you can call +Or in the debugger, anytime you have a `UElement`, you can call `UElement.asRecursiveLogString` on it, evaluate and see what you find. For example, for the following Kotlin code: @@ -209,7 +208,7 @@ ## Visiting You generally shouldn't visit a source file on your own. Lint has a -special `UElementHandler` for that which is used to ensure that we don't +special `UElementHandler` for that, which is used to ensure we don't repeat visiting a source file thousands of times, one per detector. But when you're doing local analysis, you sometimes need to visit a @@ -246,19 +245,19 @@ We have our UAST tree in the top right corner. And here's the Java PSI AST behind the scenes. We can access the underlying PSI node for a -UElement by accessing the sourcePsi element. So when you do need to dip +`UElement` by accessing the `sourcePsi` property. So when you do need to dip into something language specific, that's trivial to do. Note that in some cases, these references are null. -Each of the UElement nodes point back into the PSI AST - whether a Java +Most `UElement` nodes point back to the PSI AST - whether a Java AST or a Kotlin AST. Here's the same AST, but with the **type** of the -`sourcePsi` attribute for each node added. +`sourcePsi` property for each node added. ![](images/uast-sourcepsi-type.png) -You can see that the class generated to represent the top level -functions here doesn't have a non-null `sourcePsi`, because in the +You can see that the facade class generated to contain the top level +functions has a null `sourcePsi`, because in the Kotlin PSI, there is no real `KtClass` for a facade class. And for the three members, the private field and the getter and the setter, they all correspond to the exact same, single `KtProperty` instance, the single @@ -288,8 +287,8 @@ across the languages. Declarations. Function calls. Super classes. Assignments. If expressions. Return statements. And on and on. -There *are* lint checks which are language specific -- for example, if -you write a lint check which forbids the use of companion objects -- in +There *are* lint checks that are language specific -- for example, if +you write a lint check that forbids the use of companion objects -- in that case, there's no big advantage to using UAST over PSI; it's only ever going to run on Kotlin code. (Note however that lint's APIs and convenience callbacks are all targeting UAST, so it's easier to write @@ -308,10 +307,10 @@ language specific, and where the language details aren't exposed in UAST. For example, let's say you need to determine if a `UClass` is a Kotlin -"companion object“. You could cheat and look at the class name to see if -it's ”Companion“. But that's not quite right; in Kotlin you can +“companion object”. You could cheat and look at the class name to see if +it's “Companion”. But that's not quite right; in Kotlin you can specify a custom companion object name, and of course users are free -to create classes named ”Companion“ that aren't companion objects: +to create classes named “Companion” that aren't companion objects: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin class Test { @@ -323,8 +322,8 @@ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The right way to do this, is using Kotlin PSI, via the -`UElement.sourcePsi` attribute: +The right way to do this is using Kotlin PSI, via the +`UElement.sourcePsi` property: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin // Skip companion objects @@ -335,7 +334,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (To figure out how to write the above code, use a debugger on a test -case and look at the `UClass.sourcePsi` attribute; you'll discover that +case and look at the `UClass.sourcePsi` property; you'll discover that it's some subclass of `KtObjectDeclaration`; look up its most general super interface or class, and then use code completion to discover available APIs, such as `isCompanion()`.) @@ -350,7 +349,7 @@ Lint doesn't actually give you access to everything you need if you want to try to look up types in Kotlin PSI; you need something called the "binding context”, which is not exposed anywhere! And this omission is -deliberate, because that was an implementation detail of the old +deliberate, because this is an implementation detail of the old compiler. The future is K2; a complete rewrite of the compiler front end, which is no longer using the old binding context. And as part of the tooling support for K2, there's a new API called the “Kotlin @@ -398,7 +397,7 @@ Before the Kotlin lint analysis API, lint didn't have a way to reason about the `Nothing` type. UAST only returns Java types, which maps to -void. So instead, lint had an ugly hack which just hardcoded well known +void. So instead, lint had an ugly hack that just hardcoded well known names of methods that don't return: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin @@ -441,7 +440,7 @@ Here, we have a `KtCallExpression`, and inside the `analyze` block we can call `resolveCall()` on it to reach the called method's symbol. -Similarly on a `KtDeclaration` (such as a named function or property) I +Similarly, on a `KtDeclaration` (such as a named function or property) I can call `getSymbol()` to get the symbol for that method or property, to for example look up parameter information. And on a `KtExpression` (such as an if statement) I can call `getKtType()` to get the Kotlin type. @@ -452,7 +451,7 @@ so on. In the new implementation of `callNeverReturns`, we resolve the call, -look up the corresponding function which of course is a `KtSymbol` +look up the corresponding function, which of course is a `KtSymbol` itself, and from that we get the return type, and then we can just check if it's the `Nothing` type. diff --git a/docs/changes.md.html b/docs/changes.md.html index b8f9f5ab..e3443260 100644 --- a/docs/changes.md.html +++ b/docs/changes.md.html @@ -12,6 +12,8 @@ ## Documentation Changes +2024/07/02: New documentation on AST analysis and the Kotlin Analysis API + 2022/06/29: Added documentation on how to write error messages, and fixed a bug which had left chapters 7-11 missing from the api-guide (the chapters on partial analysis, the dataflow analyzer, annotations and options). diff --git a/docs/index.html b/docs/index.html index cb82870c..3719b1e8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -87,6 +87,8 @@

  • Analyzing data flow
  • Publishing a Lint check +
  • +
  • AST Analysis
  • Unit Testing
  • @@ -155,4 +157,4 @@

    -

    formatted by Markdeep 1.16  
    \ No newline at end of file +

    formatted by Markdeep 1.17  
    \ No newline at end of file