├── .gitignore ├── Tests ├── files │ ├── sample.wl │ └── pacletinfo.m ├── CodeInspector.mt ├── TestSuite.mt ├── TokenRules.mt ├── ConcreteRules.mt ├── Confirm.mt ├── AggregateRules.mt └── AbstractRules.mt ├── docs ├── collatz.png ├── summarize.png ├── formatting.md ├── compatibility.md ├── docs.md ├── explanations.md └── comments.md ├── .WolframResources ├── WolframLanguageSyntax └── Data │ ├── UnsupportedLongNames.wl │ ├── SpecialLongNames.wl │ ├── UnsupportedCharacters.wl │ ├── SpecialCharacters.wl │ ├── SessionSymbols.wl │ ├── BadSymbols.wl │ ├── UndocumentedLongNames.wl │ ├── UndocumentedCharacters.wl │ ├── SystemLongNames.wl │ ├── ObsoleteSymbols.wl │ ├── SystemCharacters.wl │ ├── Constants.wl │ └── UndocumentedSymbols.wl ├── .project ├── cmake ├── ReplacePacletInfo.cmake ├── InstallPaclet.cmake ├── InspectFile.cmake ├── PacletInfo.cmake ├── WolframScript.cmake └── WolframKernel.cmake ├── LICENSE ├── HowToBuild.md ├── CodeInspector ├── Resources │ └── Examples │ │ └── Collatz.m ├── PacletInfo.wl.in ├── Kernel │ ├── MessageStack.wl │ ├── External.wl │ ├── TokenRules.wl │ ├── BracketMismatches.wl │ └── SuppressedRegions.wl ├── Documentation │ └── English │ │ └── Guides │ │ └── CodeInspector.nb └── Generate │ ├── MakeLinterUIDockedCell.wl │ └── MakeCodeAnalysisOptionsPalette.wl ├── CONTRIBUTING.md ├── CodeTools └── Generate │ ├── CreatePacletArchive.wl │ └── GenerateSources.wl ├── README.md ├── scripts └── re_build_CodeInspector.xml └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | build* 3 | 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /Tests/files/sample.wl: -------------------------------------------------------------------------------- 1 | 2 | Begin["Private`"] 3 | 4 | End[] 5 | -------------------------------------------------------------------------------- /docs/collatz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/codeinspector/master/docs/collatz.png -------------------------------------------------------------------------------- /docs/summarize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/codeinspector/master/docs/summarize.png -------------------------------------------------------------------------------- /Tests/CodeInspector.mt: -------------------------------------------------------------------------------- 1 | 2 | Needs["CodeInspector`"] 3 | 4 | Test[ 5 | CodeInspect[{}] 6 | , 7 | {} 8 | , 9 | TestID->"CodeInspector-20210902-S1A2W6" 10 | ] 11 | -------------------------------------------------------------------------------- /Tests/TestSuite.mt: -------------------------------------------------------------------------------- 1 | 2 | TestSuite[{ 3 | "AbstractRules.mt", 4 | "AggregateRules.mt", 5 | "CodeInspector.mt", 6 | "ConcreteRules.mt", 7 | "Confirm.mt", 8 | "TokenRules.mt" 9 | }] 10 | -------------------------------------------------------------------------------- /.WolframResources: -------------------------------------------------------------------------------- 1 | Resources[ 2 | Version[1], 3 | ExecutionBuildCommand["<"TokenRules-20190522-S2L6J4" 16 | ] 17 | 18 | EndTestSection[] 19 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | CodeInspector 4 | 5 | 6 | 7 | 8 | 9 | com.wolfram.eclipse.MEET.MathematicaProjectBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.wolfram.eclipse.MEET.SimpleMathematicaNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/BadSymbols.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"AllFalse", "AnyFalse", "Boolean", "Cloudbase", "ComplexQ", "DataSet", 5 | "ExpandFilename", "ExportPacket", "Failed", "FalseQ", 6 | "InterpolationFunction", "InterpolationPolynomial", "Match", 7 | "OptionPattern", "OptionsQ", "RationalQ", "RealQ", "StringMatch", "SymbolQ", 8 | "UnSameQ", "UrlExecute", "$PathNameSeparator", "$RegisteredUsername"} 9 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/UndocumentedLongNames.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"Akuz", "Andy", "ContinuedFractionK", "Curl", "Divergence", "DivisionSlash", 5 | "ExpectationE", "FreeformPrompt", "Gradient", "Laplacian", "Minus", "Moon", 6 | "NumberComma", "PageBreakAbove", "PageBreakBelow", "ProbabilityPr", 7 | "Spooky", "StepperDown", "StepperLeft", "StepperRight", "StepperUp", "Sun", 8 | "UnknownGlyph", "Villa", "WolframAlphaPrompt"} 9 | -------------------------------------------------------------------------------- /Tests/files/pacletinfo.m: -------------------------------------------------------------------------------- 1 | <<<<<<< HEAD:PacletInfo.m 2 | (* ::Package:: *) 3 | 4 | ======= 5 | >>>>>>> dev/HoudiniLink/master:HoudiniLink/PacletInfo.m 6 | (* Paclet Info File *) 7 | 8 | (* created 2016/08/16*) 9 | 10 | Paclet[ 11 | Name -> "HoudiniLink", 12 | Version -> "0.0.1", 13 | MathematicaVersion -> "11+", 14 | Extensions -> 15 | { 16 | {"Documentation", Language -> "English"} 17 | } 18 | ] 19 | <<<<<<< HEAD:PacletInfo.m 20 | ======= 21 | 22 | 23 | >>>>>>> dev/HoudiniLink/master:HoudiniLink/PacletInfo.m 24 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/UndocumentedCharacters.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"\\[Akuz]", "\\[Andy]", "\\[ContinuedFractionK]", "\\[Curl]", 5 | "\\[Divergence]", "\\[DivisionSlash]", "\\[ExpectationE]", 6 | "\\[FreeformPrompt]", "\\[Gradient]", "\\[Laplacian]", "\\[Minus]", 7 | "\\[Moon]", "\\[NumberComma]", "\\[PageBreakAbove]", "\\[PageBreakBelow]", 8 | "\\[ProbabilityPr]", "\\[Spooky]", "\\[StepperDown]", "\\[StepperLeft]", 9 | "\\[StepperRight]", "\\[StepperUp]", "\\[Sun]", "\\[UnknownGlyph]", 10 | "\\[Villa]", "\\[WolframAlphaPrompt]"} 11 | -------------------------------------------------------------------------------- /docs/compatibility.md: -------------------------------------------------------------------------------- 1 | 2 | # Compatibility 3 | 4 | 5 | ## Source Compatibility 6 | 7 | CodeInspector has source compatibility with 11.0+ 8 | 9 | 10 | ## FrontEnd Compatibility 11 | 12 | Any source .wl files that have `(* ::Package::"Tags" *)` or `(* ::Code::Initialization::"Tags" *)` syntax may only be edited with a version 12.3+ FE 13 | 14 | 15 | ## WolframVersion 16 | 17 | WolframVersion in PacletInfo is 12.1+ to maintain the same minimum required version as CodeParser 18 | 19 | 20 | ## Earlier Versions 21 | 22 | Manually modify WolframVersion in PacletInfo.wl to allow the paclet to be used. 23 | 24 | The message that you get when you install the paclet: 25 | ``` 26 | The paclet CodeParser was successfully installed. 27 | ``` 28 | does not necessarily mean that the paclet can be used. 29 | 30 | Make sure that the correct WolframVersion is specified. 31 | 32 | 33 | ## WolframLanguageSyntax Compatibility 34 | 35 | Only the latest released version of Wolfram System is supported. 36 | -------------------------------------------------------------------------------- /cmake/ReplacePacletInfo.cmake: -------------------------------------------------------------------------------- 1 | 2 | file(READ ${PACLETINFO_IN_SOURCE} filedata) 3 | 4 | string(TIMESTAMP DATESTRING "%a %d %b %Y %H:%M:%S") 5 | 6 | string(REGEX REPLACE "BuildDate -> \"[a-zA-Z0-9 :]*\"" "BuildDate -> \"${DATESTRING}\"" filedata ${filedata}) 7 | 8 | string(REGEX REPLACE "BuildNumber -> [0-9]+" "BuildNumber -> ${BUILDNUMBER}" filedata ${filedata}) 9 | 10 | string(REGEX REPLACE "BuildWolframVersionNumber -> [0-9]+" "BuildWolframVersionNumber -> ${VERSION_NUMBER}" filedata ${filedata}) 11 | 12 | string(REGEX REPLACE "BuildWolframLibraryVersion -> [0-9]+" "BuildWolframLibraryVersion -> ${WOLFRAMLIBRARY_VERSION}" filedata ${filedata}) 13 | 14 | string(REGEX REPLACE "Transport -> \"[a-zA-Z]*\"" "Transport -> \"${TRANSPORT}\"" filedata ${filedata}) 15 | 16 | if(LOCAL_BUILD) 17 | 18 | string(REGEX REPLACE "Version -> \"[0-9\\.]+\"," "Version -> \"${LOCAL_BUILD_VERSION}\"(* local build *)," filedata ${filedata}) 19 | 20 | endif() 21 | 22 | file(WRITE ${REPLACED_PACLETINFO} "${filedata}") 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Wolfram Research Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /cmake/InstallPaclet.cmake: -------------------------------------------------------------------------------- 1 | 2 | if(NOT EXISTS ${WOLFRAMKERNEL}) 3 | message(FATAL_ERROR "WOLFRAMKERNEL does not exist. WOLFRAMKERNEL: ${WOLFRAMKERNEL}") 4 | endif() 5 | 6 | set(CODE "\ 7 | Print[OutputForm[\"Calling PacletInstall...\"]]\; 8 | Check[ 9 | res = PacletInstall[\"${PACLET_OUTPUT}\", ForceVersionInstall -> True]\; 10 | , 11 | Print[OutputForm[Row[{\"$VersionNumber: \", NumberForm[$VersionNumber, {2, 1}]}]]]\; 12 | Print[OutputForm[Row[{\"Paclet WolframVersion: \", \"${PACLET_WOLFRAMVERSION}\"}]]]\; 13 | Print[OutputForm[Row[{\"To prevent this PacletInstall::compat message, update PacletInfo.wl.in with WolframVersion -> \\\"\", NumberForm[$VersionNumber, {2, 1}] ,\"\\\" and build and install again.\"}]]]; 14 | res 15 | , 16 | {PacletInstall::compat} 17 | ]\; 18 | Print[res //OutputForm]\; 19 | Print[OutputForm[\"Done PacletInstall\"]]\; 20 | If[!PacletObjectQ[res], 21 | Exit[1] 22 | ]\; 23 | Exit[0] 24 | ") 25 | 26 | execute_process( 27 | COMMAND 28 | ${WOLFRAMKERNEL} -noinit -noprompt -run ${CODE} 29 | TIMEOUT 30 | ${KERNEL_TIMEOUT} 31 | RESULT_VARIABLE 32 | INSTALL_RESULT 33 | ) 34 | 35 | if(NOT ${INSTALL_RESULT} EQUAL "0") 36 | message(FATAL_ERROR "Bad exit code from install: ${INSTALL_RESULT}") 37 | endif() 38 | -------------------------------------------------------------------------------- /cmake/InspectFile.cmake: -------------------------------------------------------------------------------- 1 | 2 | if(NOT CODEPARSER_EXE) 3 | return() 4 | endif() 5 | 6 | if(NOT EXISTS ${CODEPARSER_EXE}) 7 | return() 8 | endif() 9 | 10 | execute_process( 11 | COMMAND 12 | ${CODEPARSER_EXE} -check -file ${SRC} 13 | RESULT_VARIABLE 14 | CODEPARSER_RESULT 15 | ) 16 | 17 | if(${CODEPARSER_RESULT} EQUAL "0") 18 | return() 19 | endif() 20 | 21 | if(NOT ${CODEPARSER_RESULT} EQUAL "1") 22 | message(WARNING "Internal error. CODEPARSER_RESULT: ${CODEPARSER_RESULT}") 23 | return() 24 | endif() 25 | 26 | # 27 | # We know there was some problem, so now use CodeInspector to report the problem 28 | # 29 | 30 | if(NOT WOLFRAMKERNEL) 31 | return() 32 | endif() 33 | 34 | if(NOT EXISTS ${WOLFRAMKERNEL}) 35 | return() 36 | endif() 37 | 38 | set(CODE "\ 39 | If[FailureQ[FindFile[\"CodeInspector`\"]], Exit[0]]\;\ 40 | Needs[\"CodeInspector`\"]\;\ 41 | Print[\"Code inspection...\" //OutputForm]\;\ 42 | Print[CodeInspector`CodeInspectSummarize[File[\"${SRC}\"]] //OutputForm]\;\ 43 | Exit[1]\ 44 | ") 45 | 46 | execute_process( 47 | COMMAND 48 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -run ${CODE} 49 | TIMEOUT 50 | ${KERNEL_TIMEOUT} 51 | ) 52 | 53 | message(FATAL_ERROR "File had fatal errors: ${SRC}") 54 | -------------------------------------------------------------------------------- /HowToBuild.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | CodeInspector uses a Wolfram Language kernel to build a `.paclet` file. 4 | 5 | CodeInspector uses CMake to generate build scripts. 6 | 7 | Here is an example transcript using the default make generator to build CodeInspector: 8 | ``` 9 | cd codeinspector 10 | mkdir build 11 | cd build 12 | cmake .. 13 | cmake --build . 14 | ``` 15 | 16 | The result is a directory named `paclet` that contains the WL package source code and a built CodeInspector `.paclet` file for installing. 17 | 18 | Inside a kernel session you may then install the paclet by evaluating: 19 | ``` 20 | PacletInstall["/path/to/build/paclet/CodeInspector-1.9.paclet"] 21 | ``` 22 | 23 | Specify `MATHEMATICA_INSTALL_DIR` if you have Wolfram System installed in a non-default location: 24 | ``` 25 | cmake -DMATHEMATICA_INSTALL_DIR=/Applications/Mathematica.app/Contents/ .. 26 | cmake --build . 27 | ``` 28 | 29 | On Windows: 30 | ``` 31 | cmake -DMATHEMATICA_INSTALL_DIR="C:/Program Files/Wolfram Research/Mathematica/13.1" .. 32 | cmake --build . 33 | ``` 34 | 35 | ## Installing 36 | 37 | You can install the paclet from CMake: 38 | ``` 39 | cmake --install . 40 | ``` 41 | 42 | This starts a kernel and calls `PacletInstall` with the built .paclet file. 43 | -------------------------------------------------------------------------------- /CodeInspector/Resources/Examples/Collatz.m: -------------------------------------------------------------------------------- 1 | BeginPackage["Collatz`"] 2 | 3 | Collatz::usage "Collatz[n] gives a list of the iterates in the 3n+1 problem, 4 | starting from n. The conjecture is that this sequence always 5 | terminates." 6 | (*intentional implicit times*) 7 | 8 | Begin["`Private`"] 9 | 10 | Collatz[1] := {1}; 11 | 12 | Collatz[n_Integer] := Prepend[Collatz[(3 n + 1)/2], n] /; OddQ[n] && n > 0;; 13 | (*intentional ;;*) 14 | 15 | Collatz[n_Integer] := Prepend[Collatz[n/2], n] /; EvenQ[n] && n > 0; 16 | 17 | 18 | (* 19 | The call DummyFunction1[] can be replaced with CallSite[DummyFunction1[]] when profiling 20 | to enable CallSite analysis. 21 | 22 | CallSite analysis enables the profiling of time between when a function is called to when its body is entered. 23 | In this example, the Pause[0.01] would be kept track of. 24 | 25 | The CallSite wrapper is removed during instrumentation and does not affect the result. 26 | 27 | Make sure to call InstrumentProfile with the updated code and to reload the packages under profile. 28 | *) 29 | Collatz[n_Integer] := (DummyFunction1[];Prepend[Collatz[3 n + 1], n]) /; OddQ[n] && n > 0; 30 | 31 | 32 | DummyFunction1[] /; (Pause[0.01];True) := 33 | Module[{}, 34 | Null 35 | ] 36 | 37 | 38 | End[ ] 39 | 40 | EndPackage[ ] -------------------------------------------------------------------------------- /cmake/PacletInfo.cmake: -------------------------------------------------------------------------------- 1 | 2 | macro(CheckPacletInfo) 3 | 4 | if(NOT EXISTS ${WOLFRAMKERNEL}) 5 | message(FATAL_ERROR "WOLFRAMKERNEL does not exist. WOLFRAMKERNEL: ${WOLFRAMKERNEL}") 6 | endif() 7 | 8 | if(LOCAL_BUILD) 9 | message(STATUS "Paclet Version ignored in local build") 10 | set(LOCAL_BUILD_VERSION 999.9) 11 | else() 12 | # 13 | # if not local build, then get Version from PacletInfo.wl 14 | # 15 | execute_process( 16 | COMMAND 17 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -runfirst Pause[${KERNEL_PAUSE}]\;Print[OutputForm[Row[{Version,\ ";",\ WolframVersion}\ /.\ List\ @@\ Get["${PACLETINFO_IN_SOURCE}"]]]]\;Exit[] 18 | OUTPUT_VARIABLE 19 | PACLET_VERSIONS_LIST 20 | OUTPUT_STRIP_TRAILING_WHITESPACE 21 | WORKING_DIRECTORY 22 | ${PROJECT_SOURCE_DIR} 23 | TIMEOUT 24 | ${KERNEL_TIMEOUT} 25 | RESULT_VARIABLE 26 | PACLETINFO_RESULT 27 | ) 28 | 29 | if(NOT ${PACLETINFO_RESULT} EQUAL "0") 30 | message(FATAL_ERROR "Bad exit code from PacletInfo script: ${PACLETINFO_RESULT}") 31 | endif() 32 | 33 | list(GET PACLET_VERSIONS_LIST 0 PACLET_VERSION) 34 | list(GET PACLET_VERSIONS_LIST 1 PACLET_WOLFRAMVERSION) 35 | message(STATUS "PACLET_VERSION: ${PACLET_VERSION}") 36 | message(STATUS "PACLET_WOLFRAMVERSION: ${PACLET_WOLFRAMVERSION}") 37 | 38 | endif(LOCAL_BUILD) 39 | 40 | endmacro(CheckPacletInfo) 41 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Wolfram® 2 | 3 | Thank you for taking the time to contribute to the [Wolfram Research](https://github.com/wolframresearch) repos on GitHub. 4 | 5 | ## Licensing of Contributions 6 | 7 | By contributing to Wolfram, you agree and affirm that: 8 | 9 | > Wolfram may release your contribution under the terms of the [MIT license](https://opensource.org/licenses/MIT); and 10 | 11 | > You have read and agreed to the [Developer Certificate of Origin](http://developercertificate.org/), version 1.1 or later. 12 | 13 | Please see [LICENSE](LICENSE) for licensing conditions pertaining 14 | to individual repositories. 15 | 16 | 17 | ## Bug reports 18 | 19 | ### Security Bugs 20 | 21 | Please **DO NOT** file a public issue regarding a security issue. 22 | Rather, send your report privately to security@wolfram.com. Security 23 | reports are appreciated and we will credit you for it. We do not offer 24 | a security bounty, but the forecast in your neighborhood will be cloudy 25 | with a chance of Wolfram schwag! 26 | 27 | ### General Bugs 28 | 29 | Please use the repository issues page to submit general bug issues. 30 | 31 | Please do not duplicate issues. 32 | 33 | Please do send a complete and well-written report to us. Note: **the 34 | thoroughness of your report will positively correlate to our willingness 35 | and ability to address it**. 36 | 37 | When reporting issues, always include: 38 | 39 | * Your version of *Mathematica*® or the Wolfram Language. 40 | * Your operating system. -------------------------------------------------------------------------------- /docs/docs.md: -------------------------------------------------------------------------------- 1 | Lint docs 2 | 3 | 4 | ## The severity levels 5 | 6 | ### Remark 7 | 8 | Issues such as character encoding assumptions, formatting issues. 9 | 10 | A character was saved as extended ASCII, and cannot be treated as UTF8. 11 | 12 | Token is not on same line as operand. 13 | 14 | 15 | ### Warning 16 | 17 | Most likely unintended uses of functions. 18 | 19 | 20 | ### Error 21 | 22 | Recoverable errors in syntax. 23 | 24 | Code that is never intentional. 25 | 26 | `"\[Alpa]"` 27 | 28 | `f[a,]` 29 | 30 | 31 | ### Fatal 32 | 33 | Unrecoverable errors in syntax. 34 | 35 | `1 + {}}` 36 | 37 | 38 | 39 | So this brings up the interesting problem of how to categorize these problems. 40 | 41 | I have settled on 2 dimensions (for now): Severity and ConfidenceLevel 42 | 43 | if something is indeed a problem, then Severity should denote how severe it is. Will it ever impact users? Will it accidentally launch nuclear warheads? 44 | 45 | ConfidenceLevel is supposed to denote the confidence that this is actually a problem. ConfidenceLevel -> 0.0 means no confidence at all, while ConfidenceLevel -> 1.0 means something like mismatched brackets in a function. 46 | 47 | Note that even "obvious" problems don't necessarily have ConfidenceLevel -> 1.0 48 | 49 | 50 | ## Rules 51 | 52 | Concrete rules: where whitespace matters 53 | issues from parser 54 | syntax errors 55 | issues with comments 56 | 57 | 58 | Aggregate rules: the operator syntax matters 59 | implicit Times across lines 60 | bad precedences 61 | 62 | 63 | Abstract rules: what most people want 64 | 65 | -------------------------------------------------------------------------------- /docs/explanations.md: -------------------------------------------------------------------------------- 1 | 2 | # Explanations for various lints 3 | 4 | ## Why am I seeing "Unexpected character: \\:0001." ? 5 | 6 | ``` 7 | In[20]:= CodeInspect["\"\\:0001\""] 8 | 9 | Out[20]= { 10 | CodeInspector`InspectionObject[ 11 | "UnexpectedCharacter", "Unexpected character: ``\\:0001``.", 12 | "Warning", 13 | Association[ 14 | CodeParser`Source -> {{1, 2}, {1, 8}}, ConfidenceLevel -> 0.95]]} 15 | ``` 16 | 17 | It's an ASCII control character that is not in common use. 18 | 19 | With high confidence, if an ASCII control character appears in code, it is the result of some bad copy-paste error originating in the FE, or a typo from the user. 20 | 21 | Note: The lint is not 100% confident. 22 | 23 | 24 | ## Why am I seeing "\*Q function in symbolic solver. Did you mean to do this?" ? 25 | 26 | email "Suggestion for linter" 11/4/2019 27 | 28 | Related bugs: 358571, 364894, 381950 29 | 30 | Users love using the “numeric" \*Q functions in the symbolic solvers, and they just don’t work. From an example today (bug 381950): 31 | 32 | ``` 33 | In[71]:= FindInstance[EvenQ[p], p, Primes] 34 | Out[71]= {} 35 | ``` 36 | 37 | To which the user says “Why doesn’t FindInstance find 2! It’s prime and Even!” 38 | 39 | The short answer is that EvenQ gets evaluated on the symbolic p and outputs False before FindInstance even starts, giving the user: 40 | 41 | ``` 42 | FindInstance[False, p, Primes] 43 | ``` 44 | 45 | This affects 46 | - Refine (Refine[EvenQ[2 p], Element[p, Integers]]) 47 | - Reduce 48 | - Solve 49 | - FindInstance (FindInstance[EvenQ@p, p, Primes]) 50 | - Assuming (Assuming[p \\[Element] Integers, EvenQ[2 p]]) 51 | 52 | etc… 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /CodeTools/Generate/CreatePacletArchive.wl: -------------------------------------------------------------------------------- 1 | 2 | If[!MemberQ[$Path, #], PrependTo[$Path, #]]&[DirectoryName[$InputFileName, 3]] 3 | 4 | BeginPackage["CodeTools`Generate`CreatePacletArchive`"] 5 | 6 | Begin["`Private`"] 7 | 8 | (* 9 | Do not allow PacletManager to participate in finding `Generate` files 10 | 11 | PacletManager will find e.g. CodeParser/Kernel/TokenEnum.wl when asked to find CodeParser`Generate`TokenEnum` 12 | 13 | related issues: PACMAN-54 14 | *) 15 | Block[{Internal`PacletFindFile = Null&}, 16 | Needs["CodeTools`Generate`GenerateSources`"]; 17 | ] 18 | If[$VersionNumber < 12.1, 19 | Needs["PacletManager`"] 20 | ] 21 | 22 | checkBuildDir[] 23 | checkPaclet[] 24 | checkPacletLayoutDir[] 25 | 26 | 27 | If[retry, 28 | (* 29 | CreatePacletArchive may be slow on RE machines, so allow re-trying if JLink connection timeout is hit 30 | 31 | Set $connectTimeout to some large value and cross fingers (default is 20) 32 | 33 | See: RE-515885 34 | *) 35 | Needs["JLink`"]; 36 | JLink`InstallJava`Private`$connectTimeout = 300.0 37 | ] 38 | 39 | 40 | generate[] := ( 41 | 42 | Print["Calling CreatePacletArchive..."]; 43 | 44 | If[$VersionNumber >= 12.1, 45 | res = System`CreatePacletArchive[FileNameJoin[{pacletLayoutDir, paclet}], FileNameJoin[{buildDir, "paclet"}]] 46 | , 47 | res = PacletManager`PackPaclet[FileNameJoin[{pacletLayoutDir, paclet}], FileNameJoin[{buildDir, "paclet"}]] 48 | ]; 49 | 50 | Print[res]; 51 | 52 | If[!StringQ[res], 53 | Quit[1] 54 | ]; 55 | 56 | Print["Done CreatePacletArchive"] 57 | ) 58 | 59 | If[!StringQ[script], 60 | Quit[1] 61 | ] 62 | If[AbsoluteFileName[script] === AbsoluteFileName[$InputFileName], 63 | generate[] 64 | ] 65 | 66 | End[] 67 | 68 | EndPackage[] 69 | -------------------------------------------------------------------------------- /CodeInspector/PacletInfo.wl.in: -------------------------------------------------------------------------------- 1 | 2 | Paclet[ 3 | Name -> "CodeInspector", 4 | Version -> "1.9", 5 | WolframVersion -> "12.1+", 6 | Description -> "Find and report problems in Wolfram Language code.", 7 | Creator -> "Brenton Bostick ", 8 | BuildDate -> "", 9 | BuildNumber -> 0, 10 | BuildWolframVersionNumber -> 0, 11 | Updating -> Automatic, 12 | Extensions -> { 13 | {"Kernel", Root -> "Kernel", Context -> "CodeInspector`"}, 14 | {"FrontEnd", Root -> "FrontEnd", Prepend -> True}, 15 | {"Documentation", Language -> All, MainPage -> "Guides/CodeInspector"}, 16 | {"Resource", Root->"Resources", 17 | Resources -> { 18 | {"Collatz","Examples/Collatz.m"}, 19 | 20 | {"AnalyzableMessages", "Data/AnalyzableMessages.wl"}, 21 | {"BadSymbols", "Data/BadSymbols.wl"}, 22 | {"BuiltinFunctions", "Data/BuiltinFunctions.wl"}, 23 | {"Options", "Data/Options.wl"}, 24 | {"Constants", "Data/Constants.wl"}, 25 | {"ExperimentalSymbols", "Data/ExperimentalSymbols.wl"}, 26 | {"FreeCharacters", "Data/FreeCharacters.wl"}, 27 | {"FreeLongNames", "Data/FreeLongNames.wl"}, 28 | {"ObsoleteSymbols", "Data/ObsoleteSymbols.wl"}, 29 | {"SessionSymbols", "Data/SessionSymbols.wl"}, 30 | {"SystemCharacters", "Data/SystemCharacters.wl"}, 31 | {"SystemLongNames", "Data/SystemLongNames.wl"}, 32 | {"UndocumentedCharacters", "Data/UndocumentedCharacters.wl"}, 33 | {"UndocumentedLongNames", "Data/UndocumentedLongNames.wl"}, 34 | {"UndocumentedSymbols", "Data/UndocumentedSymbols.wl"}, 35 | {"UnsupportedCharacters", "Data/UnsupportedCharacters.wl"}, 36 | {"UnsupportedLongNames", "Data/UnsupportedLongNames.wl"} 37 | } 38 | } 39 | } 40 | ] 41 | -------------------------------------------------------------------------------- /cmake/WolframScript.cmake: -------------------------------------------------------------------------------- 1 | 2 | if(NOT EXISTS ${WOLFRAMKERNEL}) 3 | message(FATAL_ERROR "WOLFRAMKERNEL does not exist. WOLFRAMKERNEL: ${WOLFRAMKERNEL}") 4 | endif() 5 | 6 | if(NOT DEFINED RETRY_ON_FAILURE) 7 | set(RETRY_ON_FAILURE OFF) 8 | endif() 9 | 10 | if(NOT EXISTS ${SCRIPT}) 11 | message(FATAL_ERROR "SCRIPT does not exist. SCRIPT: ${SCRIPT}") 12 | endif() 13 | 14 | file(READ ${SCRIPT} script) 15 | 16 | if(script STREQUAL "") 17 | message(FATAL_ERROR "SCRIPT is empty. SCRIPT: ${SCRIPT}") 18 | endif() 19 | 20 | if(RETRY_ON_FAILURE) 21 | 22 | # 23 | # try twice 24 | # 25 | 26 | execute_process( 27 | COMMAND 28 | ${WOLFRAMKERNEL} -script ${SCRIPT} -srcDir ${SRCDIR} -buildDir ${BUILDDIR} -pacletLayoutDir ${PACLET_LAYOUT_DIR} -paclet ${PACLET} 29 | TIMEOUT 30 | ${KERNEL_TIMEOUT} 31 | RESULT_VARIABLE 32 | SCRIPT_RESULT 33 | ) 34 | 35 | if(NOT ${SCRIPT_RESULT} EQUAL "0") 36 | message(WARNING "First try: Bad exit code from script: ${SCRIPT_RESULT}; retrying...") 37 | 38 | execute_process( 39 | COMMAND 40 | ${WOLFRAMKERNEL} -retry -script ${SCRIPT} -srcDir ${SRCDIR} -buildDir ${BUILDDIR} -pacletLayoutDir ${PACLET_LAYOUT_DIR} -paclet ${PACLET} 41 | TIMEOUT 42 | ${KERNEL_TIMEOUT} 43 | RESULT_VARIABLE 44 | SCRIPT_RESULT 45 | ) 46 | 47 | if(NOT ${SCRIPT_RESULT} EQUAL "0") 48 | message(FATAL_ERROR "Second try: Bad exit code from script: ${SCRIPT_RESULT}; stopping") 49 | else() 50 | message(STATUS "Second try: Success!") 51 | endif() 52 | 53 | endif() 54 | 55 | else(RETRY_ON_FAILURE) 56 | 57 | # 58 | # only try once 59 | # 60 | 61 | execute_process( 62 | COMMAND 63 | ${WOLFRAMKERNEL} -script ${SCRIPT} -srcDir ${SRCDIR} -buildDir ${BUILDDIR} -pacletLayoutDir ${PACLET_LAYOUT_DIR} -paclet ${PACLET} 64 | TIMEOUT 65 | ${KERNEL_TIMEOUT} 66 | RESULT_VARIABLE 67 | SCRIPT_RESULT 68 | ) 69 | 70 | if(NOT ${SCRIPT_RESULT} EQUAL "0") 71 | message(FATAL_ERROR "Bad exit code from script: ${SCRIPT_RESULT}") 72 | endif() 73 | 74 | endif() 75 | -------------------------------------------------------------------------------- /CodeTools/Generate/GenerateSources.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["CodeTools`Generate`GenerateSources`"] 2 | 3 | buildDirFlagPosition 4 | 5 | buildDir 6 | 7 | srcDirFlagPosition 8 | 9 | srcDir 10 | 11 | script 12 | 13 | pacletFlagPosition 14 | 15 | paclet 16 | 17 | retryFlagPosition 18 | 19 | retry 20 | 21 | pacletLayoutDirFlagPosition 22 | 23 | pacletLayoutDir 24 | 25 | 26 | checkBuildDir 27 | 28 | checkSrcDir 29 | 30 | checkPaclet 31 | 32 | checkPacletLayoutDir 33 | 34 | 35 | Begin["`Private`"] 36 | 37 | buildDirFlagPosition = FirstPosition[$CommandLine, "-buildDir"] 38 | 39 | buildDir := buildDir = $CommandLine[[buildDirFlagPosition[[1]] + 1]] 40 | 41 | srcDirFlagPosition = FirstPosition[$CommandLine, "-srcDir"] 42 | 43 | srcDir := srcDir = $CommandLine[[srcDirFlagPosition[[1]] + 1]] 44 | 45 | scriptPosition = FirstPosition[$CommandLine, "-script"] 46 | 47 | script := script = $CommandLine[[scriptPosition[[1]] + 1]] 48 | 49 | pacletFlagPosition = FirstPosition[$CommandLine, "-paclet"] 50 | 51 | paclet := paclet = $CommandLine[[pacletFlagPosition[[1]] + 1]] 52 | 53 | retryFlagPosition = FirstPosition[$CommandLine, "-retry"] 54 | 55 | retry = !MissingQ[retryFlagPosition] 56 | 57 | pacletLayoutDirFlagPosition = FirstPosition[$CommandLine, "-pacletLayoutDir"] 58 | 59 | pacletLayoutDir := pacletLayoutDir = $CommandLine[[pacletLayoutDirFlagPosition[[1]] + 1]] 60 | 61 | 62 | checkBuildDir[] := 63 | Module[{}, 64 | If[MissingQ[buildDirFlagPosition], 65 | Print["Cannot proceed; buildDir flag missing"]; 66 | Quit[1] 67 | ]; 68 | 69 | If[!DirectoryQ[buildDir], 70 | Print["Cannot proceed; Unsupported buildDir: ", buildDir]; 71 | Quit[1] 72 | ]; 73 | ] 74 | 75 | 76 | checkSrcDir[] := 77 | Module[{}, 78 | If[MissingQ[srcDirFlagPosition], 79 | Print["Cannot proceed; srcDir flag missing"]; 80 | Quit[1] 81 | ]; 82 | 83 | If[!DirectoryQ[srcDir], 84 | Print["Cannot proceed; Unsupported srcDir: ", srcDir]; 85 | Quit[1] 86 | ]; 87 | ] 88 | 89 | 90 | checkPaclet[] := 91 | Module[{}, 92 | If[MissingQ[pacletFlagPosition], 93 | Print["Cannot proceed; paclet flag missing"]; 94 | Quit[1] 95 | ]; 96 | ] 97 | 98 | 99 | checkPacletLayoutDir[] := 100 | Module[{}, 101 | If[MissingQ[pacletLayoutDirFlagPosition], 102 | Print["Cannot proceed; pacletLayoutDir flag missing"]; 103 | Quit[1] 104 | ]; 105 | 106 | If[!DirectoryQ[pacletLayoutDir], 107 | Print["Cannot proceed; Unsupported pacletLayoutDir: ", pacletLayoutDir]; 108 | Quit[1] 109 | ]; 110 | 111 | If[FileNameTake[pacletLayoutDir, -1] =!= "paclet", 112 | Print["Cannot proceed; Unsupported pacletLayoutDir: ", pacletLayoutDir]; 113 | Quit[1] 114 | ]; 115 | ] 116 | 117 | End[] 118 | 119 | EndPackage[] 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeInspector 2 | 3 | CodeInspector is a package for finding and reporting problems in Wolfram Language code. 4 | CodeInspector has a large collection of rules that can be used to inspect Wolfram Language source code files and can be customized to your preferences. 5 | 6 | In a stand-alone kernel: 7 | ``` 8 | Needs["CodeInspector`"] 9 | 10 | CodeInspectSummarize["If[a,b,b]"] 11 | ``` 12 | ``` 13 | Out[2]= If[a,b,b] 14 | 15 | line 1: If[a,b,b] 16 | ^ ^ 17 | DuplicateClauses Error Both branches are the same. 18 | ``` 19 | 20 | In a front end:\ 21 | ![CodeInspectSummarize](docs/summarize.png) 22 | 23 | [Static Analysis Tools in the Wolfram Language](https://blog.wolfram.com/2021/04/06/static-analysis-tools-in-the-wolfram-language/) 24 | 25 | ["CodeParser and CodeInspector" on community.wolfram.com](https://community.wolfram.com/groups/-/m/t/1931315) 26 | 27 | [Finding Bugs in the Wolfram Language from WTC 2019: Watch Video (youtube)](https://www.youtube.com/watch?v=jMUVwLglt-c) 28 | 29 | [Finding Bugs in the Wolfram Language from WTC 2019: Watch Video (wolfram.com)](https://www.wolfram.com/broadcast/video.php?v=2911) 30 | 31 | [Finding Bugs in the Wolfram Language from WTC 2019: Download Presentation](https://files.wolframcdn.com/pub/www.wolfram.com/technology-conference/2019/Thursday/2019BrentonBostickFindingBugsInTheWL.nb) 32 | 33 | 34 | ## Setup 35 | 36 | CodeInspector depends on [CodeParser paclet](https://github.com/WolframResearch/codeparser) and [CodeFormatter paclet](https://github.com/WolframResearch/codeformatter). 37 | 38 | CodeInspector and its dependencies are included in Mathematica 12.2 and above. 39 | 40 | For older versions, install CodeInspector paclet and dependencies from the public paclet server: 41 | ``` 42 | PacletInstall["CodeParser"] 43 | PacletInstall["CodeFormatter"] 44 | PacletInstall["CodeInspector"] 45 | ``` 46 | 47 | [Build and install the CodeInspector paclet locally](HowToBuild.md) 48 | 49 | 50 | ## Using CodeInspector 51 | 52 | After CodeParser and CodeInspector are installed, CodeInspector can be used. 53 | 54 | Return the list of all problems found in a string of code: 55 | ``` 56 | Needs["CodeInspector`"] 57 | 58 | CodeInspect["If[a,b,b]"] 59 | ``` 60 | ``` 61 | Out[2]= {DuplicateClauses Error Both branches are the same.} 62 | ``` 63 | 64 | Summarize the problems found in a source code file: 65 | 66 | ![Collatz](docs/collatz.png) 67 | 68 | The input to `CodeInspect` and `CodeInspectSummarize` may be a string, a `File`, or a list of bytes. 69 | 70 | See this tutorial in the Wolfram System help browser: 71 | 72 | * CodeInspector/tutorial/CodeInspectorTutorial 73 | 74 | 75 | ## Troubleshooting 76 | 77 | Make sure that the paclets can be found on your system: 78 | ``` 79 | Needs["CodeInspector`"] 80 | ``` 81 | 82 | and try a basic example: 83 | ``` 84 | CodeInspect["If[a, b, b]"] 85 | ``` 86 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/SystemLongNames.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"SystemsModelDelay", "FormalA", "FormalB", "FormalC", "FormalD", "FormalE", 5 | "FormalF", "FormalG", "FormalH", "FormalI", "FormalJ", "FormalK", "FormalL", 6 | "FormalM", "FormalN", "FormalO", "FormalP", "FormalQ", "FormalR", "FormalS", 7 | "FormalT", "FormalU", "FormalV", "FormalW", "FormalX", "FormalY", "FormalZ", 8 | "FormalCapitalA", "FormalCapitalB", "FormalCapitalC", "FormalCapitalD", 9 | "FormalCapitalE", "FormalCapitalF", "FormalCapitalG", "FormalCapitalH", 10 | "FormalCapitalI", "FormalCapitalJ", "FormalCapitalK", "FormalCapitalL", 11 | "FormalCapitalM", "FormalCapitalN", "FormalCapitalO", "FormalCapitalP", 12 | "FormalCapitalQ", "FormalCapitalR", "FormalCapitalS", "FormalCapitalT", 13 | "FormalCapitalU", "FormalCapitalV", "FormalCapitalW", "FormalCapitalX", 14 | "FormalCapitalY", "FormalCapitalZ", "FormalCapitalAlpha", 15 | "FormalCapitalBeta", "FormalCapitalGamma", "FormalCapitalDelta", 16 | "FormalCapitalEpsilon", "FormalCapitalZeta", "FormalCapitalEta", 17 | "FormalCapitalTheta", "FormalCapitalIota", "FormalCapitalKappa", 18 | "FormalCapitalLambda", "FormalCapitalMu", "FormalCapitalNu", 19 | "FormalCapitalXi", "FormalCapitalOmicron", "FormalCapitalPi", 20 | "FormalCapitalRho", "FormalCapitalSigma", "FormalCapitalTau", 21 | "FormalCapitalUpsilon", "FormalCapitalPhi", "FormalCapitalChi", 22 | "FormalCapitalPsi", "FormalCapitalOmega", "FormalAlpha", "FormalBeta", 23 | "FormalGamma", "FormalDelta", "FormalCurlyEpsilon", "FormalZeta", 24 | "FormalEta", "FormalTheta", "FormalIota", "FormalKappa", "FormalLambda", 25 | "FormalMu", "FormalNu", "FormalXi", "FormalOmicron", "FormalPi", 26 | "FormalRho", "FormalFinalSigma", "FormalSigma", "FormalTau", 27 | "FormalUpsilon", "FormalCurlyPhi", "FormalChi", "FormalPsi", "FormalOmega", 28 | "FormalCurlyTheta", "FormalCurlyCapitalUpsilon", "FormalPhi", 29 | "FormalCurlyPi", "FormalCapitalStigma", "FormalStigma", 30 | "FormalCapitalDigamma", "FormalDigamma", "FormalCapitalKoppa", 31 | "FormalKoppa", "FormalCapitalSampi", "FormalSampi", "FormalCurlyKappa", 32 | "FormalCurlyRho", "FormalEpsilon", "FormalScriptA", "FormalScriptB", 33 | "FormalScriptC", "FormalScriptD", "FormalScriptE", "FormalScriptF", 34 | "FormalScriptG", "FormalScriptH", "FormalScriptI", "FormalScriptJ", 35 | "FormalScriptK", "FormalScriptL", "FormalScriptM", "FormalScriptN", 36 | "FormalScriptO", "FormalScriptP", "FormalScriptQ", "FormalScriptR", 37 | "FormalScriptS", "FormalScriptT", "FormalScriptU", "FormalScriptV", 38 | "FormalScriptW", "FormalScriptX", "FormalScriptY", "FormalScriptZ", 39 | "FormalScriptCapitalA", "FormalScriptCapitalB", "FormalScriptCapitalC", 40 | "FormalScriptCapitalD", "FormalScriptCapitalE", "FormalScriptCapitalF", 41 | "FormalScriptCapitalG", "FormalScriptCapitalH", "FormalScriptCapitalI", 42 | "FormalScriptCapitalJ", "FormalScriptCapitalK", "FormalScriptCapitalL", 43 | "FormalScriptCapitalM", "FormalScriptCapitalN", "FormalScriptCapitalO", 44 | "FormalScriptCapitalP", "FormalScriptCapitalQ", "FormalScriptCapitalR", 45 | "FormalScriptCapitalS", "FormalScriptCapitalT", "FormalScriptCapitalU", 46 | "FormalScriptCapitalV", "FormalScriptCapitalW", "FormalScriptCapitalX", 47 | "FormalScriptCapitalY", "FormalScriptCapitalZ"} 48 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/ObsoleteSymbols.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"AbortScheduledTask", "Active", "AlgebraicRules", "AlternateImage", 5 | "AnatomyForm", "AnimationCycleOffset", "AnimationCycleRepetitions", 6 | "AnimationDisplayTime", "AspectRatioFixed", "AstronomicalData", 7 | "AsynchronousTaskObject", "AsynchronousTasks", "AudioDevice", 8 | "AudioLooping", "ButtonEvaluator", "ButtonExpandable", "ButtonFrame", 9 | "ButtonMargins", "ButtonNote", "ButtonStyle", "CDFInformation", 10 | "ChebyshevDistance", "ClassifierInformation", "ClipFill", "ColorOutput", 11 | "ColumnForm", "Compose", "ConstantArrayLayer", "ConstantPlusLayer", 12 | "ConstantTimesLayer", "ConstrainedMax", "ConstrainedMin", "ContourGraphics", 13 | "ContourLines", "ConversionOptions", "CreateScheduledTask", 14 | "CreateTemporary", "Curry", "DatabinRemove", "Date", "Debug", 15 | "DefaultColor", "DefaultFont", "DensityGraphics", "Display", 16 | "DisplayString", "DotPlusLayer", "DragAndDrop", "EdgeLabeling", 17 | "EdgeRenderingFunction", "EvaluateScheduledTask", "ExpectedValue", 18 | "FactorComplete", "FontForm", "FormTheme", "FromDate", "FullOptions", 19 | "GraphicsArray", "GraphicsSpacing", "GraphStyle", "GridBaseline", 20 | "HeldPart", "HiddenSurface", "HomeDirectory", "HTMLSave", "ImageRotated", 21 | "InstanceNormalizationLayer", "LegendreType", "LightSources", 22 | "LinearProgramming", "LinkOpen", "Literal", "LongestMatch", 23 | "LUBackSubstitution", "MeshRange", "MoleculeEquivalentQ", "NetInformation", 24 | "NetSharedArray", "NextScheduledTaskTime", "NotebookCreate", 25 | "OpenTemporary", "PackingMethod", "PersistentValue", "PIDData", 26 | "PixelConstrained", "Plot3Matrix", "PlotDivision", "PlotJoined", 27 | "PolygonIntersections", "PredictorInformation", "Properties", "Property", 28 | "PropertyList", "PropertyValue", "Random", "RasterArray", 29 | "RecognitionThreshold", "Release", "RemoteKernelObject", 30 | "RemoveAsynchronousTask", "RemoveProperty", "RemoveScheduledTask", 31 | "RenderAll", "ReplaceHeldPart", "ResetScheduledTask", "ResumePacket", 32 | "RunScheduledTask", "ScheduledTaskActiveQ", "ScheduledTaskInformation", 33 | "ScheduledTaskObject", "ScheduledTasks", "ScreenRectangle", 34 | "SelectionAnimate", "SequenceAttentionLayer", "SequenceForm", "SetProperty", 35 | "Shading", "ShortestMatch", "SingularValues", "SkinStyle", 36 | "SocialMediaData", "StartAsynchronousTask", "StartScheduledTask", 37 | "StateDimensions", "StopAsynchronousTask", "StopScheduledTask", 38 | "StructuredArray", "StyleForm", "StylePrint", "Subscripted", "SurfaceColor", 39 | "SurfaceGraphics", "SuspendPacket", "SystemModelProgressReporting", 40 | "TeXSave", "TextStyle", "TimeWarpingCorrespondence", "TimeWarpingDistance", 41 | "ToDate", "ToFileName", "ToHeldExpression", "URLFetch", 42 | "URLFetchAsynchronous", "URLSave", "URLSaveAsynchronous", "VectorScale", 43 | "VertexCoordinateRules", "VertexLabeling", "VertexRenderingFunction", 44 | "WaitAsynchronousTask", "WindowMovable", "$AsynchronousTask", 45 | "$ConfiguredKernels", "$DefaultFont", "$EntityStores", "$FormatType", 46 | "$HTTPCookies", "$InstallationDate", "$MachineDomain", 47 | "$ProductInformation", "$ProgramName", "$RandomState", "$ScheduledTask", 48 | "$SummaryBoxDataSizeLimit", "$TemporaryPrefix", "$TextStyle", 49 | "$TopDirectory", "$UserAddOnsDirectory"} 50 | -------------------------------------------------------------------------------- /Tests/ConcreteRules.mt: -------------------------------------------------------------------------------- 1 | 2 | Needs["CodeInspector`"] 3 | 4 | 5 | 6 | (* 7 | ImplicitTimesAcrossLines 8 | *) 9 | TestMatch[ 10 | CodeInspect["{ a\nb }"] 11 | , 12 | { InspectionObject["ImplicitTimesAcrossLines", _, _, _] } 13 | , 14 | TestID->"ConcreteRules-20190522-D9Q1R2" 15 | ] 16 | 17 | 18 | 19 | (* 20 | DotDifferentLine 21 | *) 22 | TestMatch[ 23 | CodeInspect["{ a \n . b }"] 24 | , 25 | { InspectionObject["DifferentLine", _, _, _] } 26 | , 27 | TestID->"ConcreteRules-20190522-U4K0M9" 28 | ] 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | (* 38 | DifferentLine 39 | *) 40 | TestMatch[ 41 | CodeInspect["(f[]\n; Throw[$Failed, $tag])"] 42 | , 43 | { InspectionObject["DifferentLine", _, "Remark", _] } 44 | , 45 | TestID->"ConcreteRules-20190522-I8L1E6" 46 | ] 47 | 48 | 49 | (* 50 | DifferentLine: 51 | *) 52 | 53 | TestMatch[ 54 | CodeInspect["-\na"] 55 | , 56 | {InspectionObject["DifferentLine", _, _, _]} 57 | , 58 | TestID->"ConcreteRules-20190521-Y8O9L2" 59 | ] 60 | 61 | TestMatch[ 62 | CodeInspect["{ a\n! }"] 63 | , 64 | {InspectionObject["DifferentLine", _, _, _]} 65 | , 66 | TestID->"ConcreteRules-20190521-R2X2T0" 67 | ] 68 | 69 | (* 70 | 71 | TODO: should we have "DifferentLine" warnings about ; ? 72 | 73 | TestMatch[ 74 | FirstCase[ConcreteParseString["{ a\n; }", HoldNode[Hold, #[[1]], <|SyntaxIssues -> #[[3]]|>] &], 75 | KeyValuePattern[SyntaxIssues -> _], $Failed, {0, Infinity}] 76 | , 77 | KeyValuePattern[SyntaxIssues -> {SyntaxIssue["DifferentLine", _, _, _]}] 78 | , 79 | TestID->"SyntaxIssues-20190521-M5K3M2" 80 | ] 81 | *) 82 | 83 | TestMatch[ 84 | CodeInspect["{ a\n;; }"] 85 | , 86 | {InspectionObject["DifferentLine", _, _, _]} 87 | , 88 | TestID->"ConcreteRules-20190521-E0L3O1" 89 | ] 90 | 91 | TestMatch[ 92 | CodeInspect["{ a~\nf~b } "] 93 | , 94 | {InspectionObject["DifferentLine", _, _, _]} 95 | , 96 | TestID->"ConcreteRules-20190521-S5U4W8" 97 | ] 98 | 99 | 100 | TestMatch[ 101 | CodeInspect["{ a~f~\nb } "] 102 | , 103 | {} 104 | , 105 | TestID->"ConcreteRules-20191212-Y2G4G8" 106 | ] 107 | 108 | 109 | TestMatch[ 110 | CodeInspect["<<\na"] 111 | , 112 | {InspectionObject["DifferentLine", _, _, _]} 113 | , 114 | TestID->"ConcreteRules-20191212-S0K7T1" 115 | ] 116 | 117 | 118 | 119 | (* 120 | ImplicitTimesSpan 121 | *) 122 | 123 | TestMatch[ 124 | CodeInspect[";;b;;"] 125 | , 126 | { InspectionObject["ImplicitTimesSpan", _, _, _] } 127 | , 128 | TestID->"ConcreteRules-20190523-I1D9N0" 129 | ] 130 | 131 | 132 | TestMatch[ 133 | CodeInspect["a;;b;;"] 134 | , 135 | { InspectionObject["ImplicitTimesSpan", _, _, _] } 136 | , 137 | TestID->"ConcreteRules-20190523-L7M6K3" 138 | ] 139 | 140 | 141 | 142 | TestMatch[ 143 | CodeInspect["a_..b"] 144 | , 145 | { InspectionObject["UnexpectedDot", _, _, _], InspectionObject["BackwardsCompatibility", _, _, _] } 146 | , 147 | TestID->"ConcreteRules-20200821-G8T2K3" 148 | ] 149 | 150 | TestMatch[ 151 | CodeInspect["a_..."] 152 | , 153 | { InspectionObject["UnexpectedDot", _, _, _], InspectionObject["BackwardsCompatibility", _, _, _] } 154 | , 155 | TestID->"ConcreteRules-20200821-R7H2A4" 156 | ] 157 | 158 | 159 | 160 | 161 | 162 | 163 | TestMatch[ 164 | CodeInspect["1.2`-3"] 165 | , 166 | { InspectionObject["UnexpectedSign", _, _, _]} 167 | , 168 | TestID->"ConcreteRules-20220316-Z1O7X6" 169 | ] 170 | 171 | TestMatch[ 172 | CodeInspect["1.2`-.3"] 173 | , 174 | { InspectionObject["UnexpectedSign", _, _, _]} 175 | , 176 | TestID->"ConcreteRules-20220316-T6F9N5" 177 | ] 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /docs/comments.md: -------------------------------------------------------------------------------- 1 | 2 | # Suppressed Regions and Suppressed Tags 3 | 4 | ## Suppressed Regions 5 | 6 | It is convenient to have a way to disable certain lints within a region of code 7 | 8 | 9 | ``` 10 | (* :!CodeAnalysis::BeginBlock:: *) 11 | (* :!CodeAnalysis::Disable::PatternRule:: *) 12 | 13 | f[a_ -> a] 14 | 15 | (* :!CodeAnalysis::EndBlock:: *) 16 | ``` 17 | 18 | 19 | 20 | ### Arguments 21 | 22 | Some lints require an additional argument: 23 | 24 | ``` 25 | (* :!CodeAnalysis::BeginBlock:: *) 26 | (* :!CodeAnalysis::Disable::DuplicateClauses::If:: *) 27 | 28 | If[a, b, b] 29 | 30 | (* :!CodeAnalysis::EndBlock:: *) 31 | ``` 32 | 33 | 34 | 35 | 36 | 37 | 38 | ## in-the-clear 39 | 40 | authored by humans in .m files 41 | 42 | from mon mar 29 sw codetools meeting 43 | 44 | (* :!CodeAnalysis::BeginBlock:: *) 45 | (* :!CodeAnalysis::Disable::PatternTest:: *) 46 | 47 | (* :!CodeAnalysis::EndBlock:: *) 48 | 49 | 50 | 51 | 52 | (* :!CodeFormatting::BeginBlock:: *) 53 | (* :!CodeFormatting::LineWidth::120:: *) 54 | 55 | (* :!CodeFormatting::EndBlock:: *) 56 | 57 | 58 | 59 | 60 | (* :!CodeInstrumenting::BeginBlock:: *) 61 | (* :!CodeInstrumenting::Ignore:: *) 62 | 63 | (* :!CodeInstrumenting::EndBlock:: *) 64 | 65 | 66 | 67 | 68 | 69 | 70 | ## in-the-blind 71 | 72 | managed by the FE 73 | 74 | CodeInspector can consume it 75 | 76 | 77 | actual: 78 | (* ::Package::"Tags"->Association["tag1" -> Association[Enabled -> True], "tag2" -> Association[Enabled -> False]]:: *) 79 | 80 | If[a, b, b] 81 | 82 | 83 | 84 | 85 | actual: 86 | 87 | (* ::Code::Initialization::"Tags"->Association["tag1" -> Association[Enabled -> True], "tag2" -> Association[Enabled -> False]]:: *) 88 | If[a, b, b] 89 | 90 | 91 | (* ::Code::Initialization::"Tags"*) only applies to next cell! 92 | 93 | 94 | 95 | 96 | 97 | 98 | (* set *) 99 | CurrentValue[$FrontEnd, {CodeAssistOptions, "CodeToolsOptions", 100 | "CodeInspect", "Tags", "tag1", Enabled}, True] = False 101 | (* query *) 102 | AbsoluteCurrentValue[$FrontEnd, {CodeAssistOptions, 103 | "CodeToolsOptions", "CodeInspect", "Tags", "tag1", Enabled}, True] 104 | 105 | Why? Partly because you don't want to be responsible for managing inheritance yourself, and this will easily navigate the inheritance issues in the new world of Association inheritance. But also partly because it's more flexible. 106 | 107 | It allows for things like the following without ambiguity: 108 | * Suppress at the global level, but reenable for a specific notebook. 109 | 110 | CurrentValue[$FrontEnd, {CodeAssistOptions, "CodeToolsOptions", 111 | "CodeInspect", "Tags", "tag1", Enabled}] = False 112 | CurrentValue[_NotebookObject, {CodeAssistOptions, "CodeToolsOptions", 113 | "CodeInspect", "Tags", "tag1", Enabled}] = True 114 | 115 | * Configure a tag with settings other than on/off. For example, if you have a tag that might have some experimental support to it, or some way of customizing how deep its scans are, whether it uses slower methods for more comprehensive checks, whether it offers fixes which might frequently proven to be questionable or of low quality, etc. 116 | CurrentValue[$FrontEnd, {CodeAssistOptions, "CodeToolsOptions", 117 | "CodeInspect", "Tags", "tag1", "OfferLowQualityFix"}] = True 118 | 119 | If you use this method, you only ever have to make one query. You query AbsoluteCurrentValue on the CellObject, with a third argument that provides the default value if the setting simply doesn't exist, and let the FE do the rest of the work for you. The FE is really *very* good at this (association-level inheritance is just a slight spin on the general inheritance mechanism the FE has used for 15 years...it is quite a battle-hardened system). 120 | 121 | -John 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/SystemCharacters.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"\\[SystemsModelDelay]", "\\[FormalA]", "\\[FormalB]", "\\[FormalC]", 5 | "\\[FormalD]", "\\[FormalE]", "\\[FormalF]", "\\[FormalG]", "\\[FormalH]", 6 | "\\[FormalI]", "\\[FormalJ]", "\\[FormalK]", "\\[FormalL]", "\\[FormalM]", 7 | "\\[FormalN]", "\\[FormalO]", "\\[FormalP]", "\\[FormalQ]", "\\[FormalR]", 8 | "\\[FormalS]", "\\[FormalT]", "\\[FormalU]", "\\[FormalV]", "\\[FormalW]", 9 | "\\[FormalX]", "\\[FormalY]", "\\[FormalZ]", "\\[FormalCapitalA]", 10 | "\\[FormalCapitalB]", "\\[FormalCapitalC]", "\\[FormalCapitalD]", 11 | "\\[FormalCapitalE]", "\\[FormalCapitalF]", "\\[FormalCapitalG]", 12 | "\\[FormalCapitalH]", "\\[FormalCapitalI]", "\\[FormalCapitalJ]", 13 | "\\[FormalCapitalK]", "\\[FormalCapitalL]", "\\[FormalCapitalM]", 14 | "\\[FormalCapitalN]", "\\[FormalCapitalO]", "\\[FormalCapitalP]", 15 | "\\[FormalCapitalQ]", "\\[FormalCapitalR]", "\\[FormalCapitalS]", 16 | "\\[FormalCapitalT]", "\\[FormalCapitalU]", "\\[FormalCapitalV]", 17 | "\\[FormalCapitalW]", "\\[FormalCapitalX]", "\\[FormalCapitalY]", 18 | "\\[FormalCapitalZ]", "\\[FormalCapitalAlpha]", "\\[FormalCapitalBeta]", 19 | "\\[FormalCapitalGamma]", "\\[FormalCapitalDelta]", 20 | "\\[FormalCapitalEpsilon]", "\\[FormalCapitalZeta]", "\\[FormalCapitalEta]", 21 | "\\[FormalCapitalTheta]", "\\[FormalCapitalIota]", "\\[FormalCapitalKappa]", 22 | "\\[FormalCapitalLambda]", "\\[FormalCapitalMu]", "\\[FormalCapitalNu]", 23 | "\\[FormalCapitalXi]", "\\[FormalCapitalOmicron]", "\\[FormalCapitalPi]", 24 | "\\[FormalCapitalRho]", "\\[FormalCapitalSigma]", "\\[FormalCapitalTau]", 25 | "\\[FormalCapitalUpsilon]", "\\[FormalCapitalPhi]", "\\[FormalCapitalChi]", 26 | "\\[FormalCapitalPsi]", "\\[FormalCapitalOmega]", "\\[FormalAlpha]", 27 | "\\[FormalBeta]", "\\[FormalGamma]", "\\[FormalDelta]", 28 | "\\[FormalCurlyEpsilon]", "\\[FormalZeta]", "\\[FormalEta]", 29 | "\\[FormalTheta]", "\\[FormalIota]", "\\[FormalKappa]", "\\[FormalLambda]", 30 | "\\[FormalMu]", "\\[FormalNu]", "\\[FormalXi]", "\\[FormalOmicron]", 31 | "\\[FormalPi]", "\\[FormalRho]", "\\[FormalFinalSigma]", "\\[FormalSigma]", 32 | "\\[FormalTau]", "\\[FormalUpsilon]", "\\[FormalCurlyPhi]", "\\[FormalChi]", 33 | "\\[FormalPsi]", "\\[FormalOmega]", "\\[FormalCurlyTheta]", 34 | "\\[FormalCurlyCapitalUpsilon]", "\\[FormalPhi]", "\\[FormalCurlyPi]", 35 | "\\[FormalCapitalStigma]", "\\[FormalStigma]", "\\[FormalCapitalDigamma]", 36 | "\\[FormalDigamma]", "\\[FormalCapitalKoppa]", "\\[FormalKoppa]", 37 | "\\[FormalCapitalSampi]", "\\[FormalSampi]", "\\[FormalCurlyKappa]", 38 | "\\[FormalCurlyRho]", "\\[FormalEpsilon]", "\\[FormalScriptA]", 39 | "\\[FormalScriptB]", "\\[FormalScriptC]", "\\[FormalScriptD]", 40 | "\\[FormalScriptE]", "\\[FormalScriptF]", "\\[FormalScriptG]", 41 | "\\[FormalScriptH]", "\\[FormalScriptI]", "\\[FormalScriptJ]", 42 | "\\[FormalScriptK]", "\\[FormalScriptL]", "\\[FormalScriptM]", 43 | "\\[FormalScriptN]", "\\[FormalScriptO]", "\\[FormalScriptP]", 44 | "\\[FormalScriptQ]", "\\[FormalScriptR]", "\\[FormalScriptS]", 45 | "\\[FormalScriptT]", "\\[FormalScriptU]", "\\[FormalScriptV]", 46 | "\\[FormalScriptW]", "\\[FormalScriptX]", "\\[FormalScriptY]", 47 | "\\[FormalScriptZ]", "\\[FormalScriptCapitalA]", "\\[FormalScriptCapitalB]", 48 | "\\[FormalScriptCapitalC]", "\\[FormalScriptCapitalD]", 49 | "\\[FormalScriptCapitalE]", "\\[FormalScriptCapitalF]", 50 | "\\[FormalScriptCapitalG]", "\\[FormalScriptCapitalH]", 51 | "\\[FormalScriptCapitalI]", "\\[FormalScriptCapitalJ]", 52 | "\\[FormalScriptCapitalK]", "\\[FormalScriptCapitalL]", 53 | "\\[FormalScriptCapitalM]", "\\[FormalScriptCapitalN]", 54 | "\\[FormalScriptCapitalO]", "\\[FormalScriptCapitalP]", 55 | "\\[FormalScriptCapitalQ]", "\\[FormalScriptCapitalR]", 56 | "\\[FormalScriptCapitalS]", "\\[FormalScriptCapitalT]", 57 | "\\[FormalScriptCapitalU]", "\\[FormalScriptCapitalV]", 58 | "\\[FormalScriptCapitalW]", "\\[FormalScriptCapitalX]", 59 | "\\[FormalScriptCapitalY]", "\\[FormalScriptCapitalZ]"} 60 | -------------------------------------------------------------------------------- /scripts/re_build_CodeInspector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /CodeInspector/Kernel/MessageStack.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["CodeInspector`MessageStack`"] 2 | 3 | codeWithMessageStackInspectAST 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | Needs["CodeInspector`"] 9 | Needs["CodeInspector`Utils`"] 10 | Needs["CodeParser`"] 11 | Needs["CodeParser`Concretify`"] 12 | Needs["CodeParser`Utils`"] 13 | 14 | 15 | codeWithMessageStackInspectAST[ast_, {HoldForm[Message[MessageName[Part, "partw"], arg1_, arg2_]], rest___}] := 16 | Catch[ 17 | Module[{partCase, children, issues, replacementNode, f, r}, 18 | 19 | partCase = FirstCase[ast, CallNode[LeafNode[Symbol, "Part", _], _, _], $Failed, Infinity]; 20 | If[FailureQ[partCase], 21 | Throw[{}] 22 | ]; 23 | 24 | children = partCase /. CallNode[LeafNode[Symbol, "Part", _], children_, _] :> children; 25 | Which[ 26 | Length[children] < 2, 27 | Throw[{}] 28 | , 29 | Length[children] > 2, 30 | 31 | f = children[[1]]; 32 | r = children[[2;;]]; 33 | 34 | replacementNode = Concretify[ContainerNode[Null, {CallNode[LeafNode[Symbol, "Indexed", <||>], {f, CallNode[LeafNode[Symbol, "List", <||>], r, <||>]}, <||>]}, <||>]][[2, 1]]; 35 | , 36 | True, 37 | replacementNode = Concretify[ContainerNode[Null, {CallNode[LeafNode[Symbol, "Indexed", <||>], children, <||>]}, <||>]][[2, 1]]; 38 | ]; 39 | 40 | issues = {}; 41 | 42 | AppendTo[issues, 43 | (* 44 | remember to do ToString[..., StandardForm] 45 | bug 429482 46 | *) 47 | InspectionObject["Part", ToString[StringForm[MessageName[General, "partw"], arg1, arg2], StandardForm], "Error", <| 48 | Source -> children[[2, 3, Key[Source]]], 49 | ConfidenceLevel -> 0.95, 50 | CodeActions -> { 51 | CodeAction["Replace with ``Indexed``", ReplaceNode, <| 52 | "ReplacementNode" -> replacementNode, Source -> partCase[[3, Key[Source]]] 53 | |>] 54 | } 55 | |>] 56 | ]; 57 | 58 | issues 59 | ]] 60 | 61 | 62 | codeWithMessageStackInspectAST[ast_, {HoldForm[Message[MessageName[sym_, tag_], args___]], rest___}] := 63 | Catch[ 64 | Module[{case, name, tryGeneral, namePat, poss, issues}, 65 | 66 | tryGeneral = False; 67 | 68 | Which[ 69 | KeyExistsQ[WolframLanguageSyntax`Generate`$analyzableMessagePositions, HoldForm[MessageName[sym, tag]]], 70 | poss = WolframLanguageSyntax`Generate`$analyzableMessagePositions[HoldForm[MessageName[sym, tag]]]; 71 | , 72 | KeyExistsQ[WolframLanguageSyntax`Generate`$analyzableMessagePositions, HoldForm[MessageName[General, tag]]], 73 | tryGeneral = True; 74 | poss = WolframLanguageSyntax`Generate`$analyzableMessagePositions[HoldForm[MessageName[General, tag]]]; 75 | , 76 | True, 77 | Throw[{}] 78 | ]; 79 | 80 | name = SymbolName[Unevaluated[sym]]; 81 | 82 | Which[ 83 | name == "Import", 84 | namePat = "Import" | "ImportString" 85 | , 86 | True, 87 | namePat = name 88 | ]; 89 | 90 | 91 | issues = {}; 92 | 93 | Function[{pos}, 94 | 95 | Module[{posReplaced}, 96 | 97 | posReplaced = pos /. s_String /; StringMatchQ[s, RegularExpression["`\\d+`"]] :> ReleaseHold[{args}[[ToExpression[StringTake[s, {2, -2}]]]]]; 98 | 99 | Switch[posReplaced, 100 | _Integer, 101 | case = FirstCase[ast, CallNode[LeafNode[Symbol, namePat, _], children_, _] :> children, $Failed, Infinity]; 102 | If[FailureQ[case], 103 | Throw[{}] 104 | ]; 105 | If[Length[case] < posReplaced, 106 | Throw[{}] 107 | ]; 108 | case = case[[posReplaced]]; 109 | AppendTo[issues, 110 | (* 111 | remember to do ToString[..., StandardForm] 112 | bug 429482 113 | *) 114 | InspectionObject[name, ToString[StringForm[If[tryGeneral, MessageName[General, tag], MessageName[sym, tag]], args], StandardForm], "Error", <|Source -> case[[3, Key[Source]]], ConfidenceLevel -> 0.95|>] 115 | ] 116 | , 117 | _Symbol, 118 | case = FirstCase[ast, CallNode[LeafNode[Symbol, namePat, _], children_, _] :> children, $Failed, Infinity]; 119 | If[FailureQ[case], 120 | Throw[{}] 121 | ]; 122 | case = FirstCase[case, CallNode[LeafNode[Symbol, "Rule", _], {LeafNode[Symbol, ToString[posReplaced], _], value_}, _] :> value, $Failed, Infinity]; 123 | If[FailureQ[case], 124 | Throw[{}] 125 | ]; 126 | AppendTo[issues, 127 | (* 128 | remember to do ToString[..., StandardForm] 129 | bug 429482 130 | *) 131 | InspectionObject[name, ToString[StringForm[If[tryGeneral, MessageName[General, tag], MessageName[sym, tag]], args], StandardForm], "Error", <|Source -> case[[3, Key[Source]]], ConfidenceLevel -> 0.95|>] 132 | ] 133 | , 134 | _, 135 | Message[codeWithMessageStackInspectAST::unhandled, posReplaced //InputForm]; 136 | Null 137 | ]; 138 | ] 139 | 140 | ] /@ poss; 141 | 142 | issues 143 | ]] 144 | 145 | 146 | End[] 147 | 148 | EndPackage[] 149 | -------------------------------------------------------------------------------- /CodeInspector/Kernel/External.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["CodeInspector`External`"] 2 | 3 | OpenInEditor 4 | 5 | 6 | $Editor 7 | 8 | 9 | Begin["`Private`"] 10 | 11 | 12 | 13 | OpenInEditor::fail = "Cannot open file in editor: `1` (`2`)" 14 | 15 | 16 | (* 17 | 18 | For "Visual Studio Code": 19 | 20 | As a 1-time setup, you must run "Shell Command: Install 'code' command in PATH" in the Command Palette of VSCode 21 | 22 | https://code.visualstudio.com/docs/setup/mac#_launching-from-the-command-line 23 | 24 | 25 | For "Visual Studio Code", the better command would be just "code" 26 | 27 | But if Mathematica is launched from Finder/LaunchServices, then PATH does not contain /usr/local/bin 28 | 29 | NOTE: launching Mathematica from Terminal would allow /usr/local/bin to be added to PATH 30 | 31 | Related links: https://mathematica.stackexchange.com/a/99708/63281 32 | 33 | 34 | Running without the shell is bad also, because it means that other commands that depend on the shell are not run 35 | 36 | e.g., specifying "python3" in the settings for Visual Studio Code may not work, and the full path to python3 may be 37 | needed 38 | 39 | 40 | *) 41 | 42 | 43 | Options[OpenInEditor] = { 44 | "Editor" -> Automatic 45 | } 46 | 47 | 48 | OpenInEditor[file_String, OptionsPattern[]] := 49 | Module[{editor, res}, 50 | 51 | editor = OptionValue["Editor"]; 52 | If[editor === Automatic, 53 | editor = $Editor 54 | ]; 55 | 56 | Switch[{$InterfaceEnvironment, editor}, 57 | {"Macintosh", "Sublime Text"}, 58 | (* 59 | Run needs spaces escaped 60 | *) 61 | Run[escapeSpaces["/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl"] <> " " <> file] 62 | , 63 | {"Macintosh", "Visual Studio Code"}, 64 | (* 65 | Run needs spaces escaped 66 | *) 67 | Run[escapeSpaces["/usr/local/bin/code"] <> " -g " <> file] 68 | , 69 | {_, "SystemOpen"}, 70 | (* 71 | SystemOpen does NOT need spaces escaped 72 | *) 73 | res = SystemOpen[file]; 74 | If[FailureQ[res], 75 | Message[OpenInEditor::fail, file, "SystemOpen"] 76 | ]; 77 | , 78 | _, 79 | (* 80 | Editor "FrontEnd" is supported in all environments. 81 | 82 | NotebookOpen does NOT need spaces escaped 83 | *) 84 | res = NotebookOpen[file]; 85 | If[FailureQ[res], 86 | Message[OpenInEditor::fail, file, "FrontEnd"] 87 | ]; 88 | ] 89 | ] 90 | 91 | 92 | OpenInEditor[file_String, line_Integer, OptionsPattern[]] := 93 | Module[{editor, res}, 94 | 95 | editor = OptionValue["Editor"]; 96 | If[editor === Automatic, 97 | editor = $Editor 98 | ]; 99 | 100 | Switch[{$InterfaceEnvironment, editor}, 101 | {"Macintosh", "Sublime Text"}, 102 | (* 103 | Run needs spaces escaped 104 | *) 105 | Run[escapeSpaces["/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl"] <> " " <> file <> ":" <> ToString[line]] 106 | , 107 | {"Macintosh", "Visual Studio Code"}, 108 | (* 109 | Run needs spaces escaped 110 | *) 111 | Run[escapeSpaces["/usr/local/bin/code"] <> " -g " <> file <> ":" <> ToString[line]] 112 | , 113 | {_, "SystemOpen"}, 114 | (* 115 | SystemOpen does NOT need spaces escaped 116 | *) 117 | res = SystemOpen[file]; 118 | If[FailureQ[res], 119 | Message[OpenInEditor::fail, file, "SystemOpen"] 120 | ]; 121 | , 122 | _, 123 | (* 124 | Editor "FrontEnd" is supported in all environments. 125 | 126 | NotebookOpen does NOT need spaces escaped 127 | *) 128 | res = NotebookOpen[file]; 129 | If[FailureQ[res], 130 | Message[OpenInEditor::fail, file, "FrontEnd"] 131 | ]; 132 | ] 133 | ] 134 | 135 | 136 | OpenInEditor[file_String, line_Integer, col_Integer, OptionsPattern[]] := 137 | Module[{editor, res}, 138 | 139 | editor = OptionValue["Editor"]; 140 | If[editor === Automatic, 141 | editor = $Editor 142 | ]; 143 | 144 | Switch[{$InterfaceEnvironment, editor}, 145 | {"Macintosh", "Sublime Text"}, 146 | (* 147 | Run needs spaces escaped 148 | *) 149 | Run[escapeSpaces["/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl"] <> " " <> file <> ":" <> ToString[line] <> ":" <> ToString[col] <> ""] 150 | , 151 | {"Macintosh", "Visual Studio Code"}, 152 | (* 153 | Run needs spaces escaped 154 | *) 155 | Run[escapeSpaces["/usr/local/bin/code"] <> " -g " <> file <> ":" <> ToString[line] <> ":" <> ToString[col] <> ""] 156 | , 157 | {_, "SystemOpen"}, 158 | (* 159 | SystemOpen does NOT need spaces escaped 160 | *) 161 | res = SystemOpen[file]; 162 | If[FailureQ[res], 163 | Message[OpenInEditor::fail, file, "SystemOpen"] 164 | ]; 165 | , 166 | _, 167 | (* 168 | Editor "FrontEnd" is supported in all environments. 169 | 170 | NotebookOpen does NOT need spaces escaped 171 | *) 172 | res = NotebookOpen[file]; 173 | If[FailureQ[res], 174 | Message[OpenInEditor::fail, file, "FrontEnd"] 175 | ]; 176 | ] 177 | ] 178 | 179 | 180 | (* 181 | assume that path does not already have escaped spaces 182 | *) 183 | escapeSpaces[path_] := StringReplace[path, " " -> "\\ "] 184 | 185 | 186 | End[] 187 | 188 | EndPackage[] 189 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/Constants.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"Constant", "Flat", "HoldAll", "HoldAllComplete", "HoldFirst", "HoldRest", 5 | "Listable", "Locked", "NHoldAll", "NHoldFirst", "NHoldRest", 6 | "NumericFunction", "OneIdentity", "Orderless", "Protected", "ReadProtected", 7 | "SequenceHold", "Stub", "Temporary", "Above", "After", "Algebraics", "All", 8 | "Anonymous", "Automatic", "Axis", "Back", "Backward", "Baseline", "Before", 9 | "Below", "Black", "Blue", "Bold", "Booleans", "Bottom", "Boxes", "Brown", 10 | "Byte", "Catalan", "CellStyle", "Center", "Character", "Complexes", 11 | "ComplexInfinity", "Cyan", "Dashed", "DefaultAxesStyle", "DefaultBaseStyle", 12 | "DefaultBoxStyle", "DefaultFaceGridsStyle", "DefaultFieldHintStyle", 13 | "DefaultFrameStyle", "DefaultFrameTicksStyle", "DefaultGridLinesStyle", 14 | "DefaultLabelStyle", "DefaultMenuStyle", "DefaultTicksStyle", 15 | "DefaultTooltipStyle", "Degree", "Delimiter", "DigitCharacter", "DotDashed", 16 | "Dotted", "E", "EndOfBuffer", "EndOfFile", "EndOfLine", "EndOfString", 17 | "EulerGamma", "Expression", "False", "FontProperties", "Forward", 18 | "ForwardBackward", "Friday", "Front", "FrontEndDynamicExpression", "Full", 19 | "General", "Glaisher", "GoldenAngle", "GoldenRatio", "Gray", "Green", 20 | "Here", "HexadecimalCharacter", "I", "Indeterminate", "Infinity", 21 | "Inherited", "Integer", "Integers", "Italic", "Khinchin", "Large", "Larger", 22 | "Left", "LetterCharacter", "LightBlue", "LightBrown", "LightCyan", 23 | "LightGray", "LightGreen", "LightMagenta", "LightOrange", "LightPink", 24 | "LightPurple", "LightRed", "LightYellow", "MachinePrecision", "Magenta", 25 | "Manual", "Medium", "MeshCellCentroid", "MeshCellMeasure", 26 | "MeshCellQuality", "Monday", "NegativeIntegers", "NegativeRationals", 27 | "NegativeReals", "None", "NonNegativeIntegers", "NonNegativeRationals", 28 | "NonNegativeReals", "NonPositiveIntegers", "NonPositiveRationals", 29 | "NonPositiveReals", "Now", "NoWhitespace", "Null", "Number", "NumberString", 30 | "Orange", "Pi", "Pink", "Plain", "PositiveIntegers", "PositiveRationals", 31 | "PositiveReals", "Primes", "PunctuationCharacter", "Purple", "Rationals", 32 | "Real", "Reals", "Record", "Red", "Right", "Saturday", "Small", "Smaller", 33 | "SpanFromAbove", "SpanFromBoth", "SpanFromLeft", "StartOfLine", 34 | "StartOfString", "String", "Struckthrough", "Sunday", "Thick", "Thin", 35 | "Thursday", "Tiny", "Today", "Tomorrow", "Top", "Transparent", "True", 36 | "Tuesday", "Undefined", "Underlined", "Wednesday", "White", "Whitespace", 37 | "WhitespaceCharacter", "Word", "WordBoundary", "WordCharacter", "Yellow", 38 | "Yesterday", "$Aborted", "$ActivationKey", "$AllowDataUpdates", 39 | "$AllowInternet", "$AssertFunction", "$Assumptions", "$AudioInputDevices", 40 | "$AudioOutputDevices", "$BaseDirectory", "$BasePacletsDirectory", 41 | "$BatchInput", "$BatchOutput", "$ByteOrdering", "$CacheBaseDirectory", 42 | "$Canceled", "$CharacterEncoding", "$CharacterEncodings", 43 | "$CloudAccountName", "$CloudBase", "$CloudConnected", 44 | "$CloudCreditsAvailable", "$CloudEvaluation", "$CloudExpressionBase", 45 | "$CloudObjectNameFormat", "$CloudObjectURLType", "$CloudRootDirectory", 46 | "$CloudSymbolBase", "$CloudUserID", "$CloudUserUUID", "$CloudVersion", 47 | "$CommandLine", "$CompilationTarget", "$Context", "$ContextAliases", 48 | "$ContextPath", "$ControlActiveSetting", "$Cookies", "$CreationDate", 49 | "$CurrentLink", "$CurrentTask", "$DateStringFormat", 50 | "$DefaultAudioInputDevice", "$DefaultAudioOutputDevice", "$DefaultFrontEnd", 51 | "$DefaultImagingDevice", "$DefaultKernels", "$DefaultLocalBase", 52 | "$DefaultLocalKernel", "$Display", "$DisplayFunction", 53 | "$DistributedContexts", "$DynamicEvaluation", "$Echo", 54 | "$EmbedCodeEnvironments", "$EmbeddableServices", "$Epilog", 55 | "$EvaluationCloudBase", "$EvaluationCloudObject", "$EvaluationEnvironment", 56 | "$ExportFormats", "$Failed", "$FontFamilies", "$FrontEnd", 57 | "$FrontEndSession", "$GeoLocation", "$GeoLocationCity", 58 | "$GeoLocationCountry", "$GeoLocationSource", "$HomeDirectory", "$IgnoreEOF", 59 | "$ImageFormattingWidth", "$ImageResolution", "$ImagingDevice", 60 | "$ImagingDevices", "$ImportFormats", "$InitialDirectory", "$Input", 61 | "$InputFileName", "$InputStreamMethods", "$Inspector", 62 | "$InstallationDirectory", "$InterpreterTypes", "$IterationLimit", 63 | "$KernelCount", "$KernelID", "$Language", "$LibraryPath", 64 | "$LicenseExpirationDate", "$LicenseID", "$LicenseServer", "$Linked", 65 | "$LocalBase", "$LocalSymbolBase", "$MachineAddresses", "$MachineDomains", 66 | "$MachineEpsilon", "$MachineID", "$MachineName", "$MachinePrecision", 67 | "$MachineType", "$MaxExtraPrecision", "$MaxMachineNumber", "$MaxNumber", 68 | "$MaxPiecewiseCases", "$MaxPrecision", "$MaxRootDegree", "$MessageGroups", 69 | "$MessageList", "$MessagePrePrint", "$Messages", "$MinMachineNumber", 70 | "$MinNumber", "$MinPrecision", "$MobilePhone", "$ModuleNumber", 71 | "$NetworkConnected", "$NewMessage", "$NewSymbol", 72 | "$NotebookInlineStorageLimit", "$Notebooks", "$NumberMarks", 73 | "$OperatingSystem", "$Output", "$OutputSizeLimit", "$OutputStreamMethods", 74 | "$Packages", "$ParentLink", "$ParentProcessID", "$PasswordFile", "$Path", 75 | "$PathnameSeparator", "$PerformanceGoal", "$Permissions", "$PlotTheme", 76 | "$Printout3DPreviewer", "$ProcessID", "$ProcessorCount", "$ProcessorType", 77 | "$ProgressReporting", "$RandomGeneratorState", "$RecursionLimit", 78 | "$ReleaseNumber", "$RequesterAddress", "$RequesterCloudUserID", 79 | "$RequesterCloudUserUUID", "$RequesterWolframID", "$RequesterWolframUUID", 80 | "$RootDirectory", "$ScriptCommandLine", "$ScriptInputString", "$Services", 81 | "$SessionID", "$SharedFunctions", "$SharedVariables", 82 | "$SoundDisplayFunction", "$SynchronousEvaluation", "$System", 83 | "$SystemCharacterEncoding", "$SystemID", "$SystemShell", "$SystemTimeZone", 84 | "$SystemWordLength", "$TemplatePath", "$TemporaryDirectory", "$TimedOut", 85 | "$TimeUnit", "$TimeZone", "$TimeZoneEntity", "$UnitSystem", "$Urgent", 86 | "$UserAgentString", "$UserBaseDirectory", "$UserBasePacletsDirectory", 87 | "$UserDocumentsDirectory", "$Username", "$UserURLBase", "$Version", 88 | "$VersionNumber", "$WolframDocumentsDirectory", "$WolframID", "$WolframUUID"} 89 | -------------------------------------------------------------------------------- /cmake/WolframKernel.cmake: -------------------------------------------------------------------------------- 1 | 2 | if(NOT DEFINED MATHEMATICA_INSTALL_DIR) 3 | if(CMAKE_HOST_WIN32) 4 | set(MATHEMATICA_INSTALL_DIR "C:/Program Files/Wolfram Research/Mathematica/13.1") 5 | elseif(CMAKE_HOST_APPLE) 6 | set(MATHEMATICA_INSTALL_DIR /Applications/Mathematica.app/Contents) 7 | else() 8 | set(MATHEMATICA_INSTALL_DIR /usr/local/Wolfram/Mathematica/13.1) 9 | endif() 10 | endif() 11 | 12 | if(CMAKE_HOST_WIN32) 13 | set(WOLFRAMKERNEL_DEFAULT ${MATHEMATICA_INSTALL_DIR}/wolfram.exe) 14 | set(WOLFRAMLIBRARY_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/IncludeFiles/C) 15 | # 16 | # in versions before 11.2, there were 2 separate paths: 17 | # SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions/mldev64/include 18 | # SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions/mldev64/lib 19 | # 20 | # starting in 11.2, the single path for MathLink includes and MathLink libs is: 21 | # SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions 22 | # 23 | if(EXISTS ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions/mldev64/include) 24 | set(MATHLINK_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions/mldev64/include) 25 | else() 26 | set(MATHLINK_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions) 27 | endif() 28 | if(EXISTS ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions/mldev64/lib) 29 | set(MATHLINK_LIB_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions/mldev64/lib) 30 | else() 31 | set(MATHLINK_LIB_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions) 32 | endif() 33 | elseif(CMAKE_HOST_APPLE) 34 | set(WOLFRAMKERNEL_DEFAULT ${MATHEMATICA_INSTALL_DIR}/MacOS/WolframKernel) 35 | set(WOLFRAMLIBRARY_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/IncludeFiles/C) 36 | set(MATHLINK_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/MacOSX-x86-64/CompilerAdditions) 37 | set(MATHLINK_LIB_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/MacOSX-x86-64/CompilerAdditions) 38 | else() 39 | set(WOLFRAMKERNEL_DEFAULT ${MATHEMATICA_INSTALL_DIR}/Executables/WolframKernel) 40 | set(WOLFRAMLIBRARY_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/IncludeFiles/C) 41 | set(MATHLINK_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Linux-x86-64/CompilerAdditions) 42 | set(MATHLINK_LIB_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Linux-x86-64/CompilerAdditions) 43 | endif() 44 | 45 | macro(CheckWolframKernel) 46 | 47 | if(NOT EXISTS ${WOLFRAMKERNEL}) 48 | message(FATAL_ERROR "WOLFRAMKERNEL does not exist. WOLFRAMKERNEL: ${WOLFRAMKERNEL}") 49 | endif() 50 | 51 | # 52 | # get $Version 53 | # 54 | execute_process( 55 | COMMAND 56 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -runfirst Pause[${KERNEL_PAUSE}]\;Print[OutputForm[$Version]]\;Exit[] 57 | OUTPUT_VARIABLE 58 | VERSION 59 | OUTPUT_STRIP_TRAILING_WHITESPACE 60 | WORKING_DIRECTORY 61 | ${PROJECT_SOURCE_DIR} 62 | TIMEOUT 63 | ${KERNEL_TIMEOUT} 64 | RESULT_VARIABLE 65 | VERSION_RESULT 66 | ) 67 | 68 | message(STATUS "VERSION: ${VERSION}") 69 | 70 | if(NOT ${VERSION_RESULT} EQUAL "0") 71 | message(WARNING "Bad exit code from Version script: ${VERSION_RESULT}; Continuing") 72 | endif() 73 | 74 | # 75 | # get $VersionNumber 76 | # 77 | execute_process( 78 | COMMAND 79 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -runfirst Pause[${KERNEL_PAUSE}]\;Print[OutputForm[Floor[100\ $VersionNumber\ +\ $ReleaseNumber]]]\;Exit[] 80 | OUTPUT_VARIABLE 81 | VERSION_NUMBER 82 | OUTPUT_STRIP_TRAILING_WHITESPACE 83 | WORKING_DIRECTORY 84 | ${PROJECT_SOURCE_DIR} 85 | TIMEOUT 86 | ${KERNEL_TIMEOUT} 87 | RESULT_VARIABLE 88 | VERSION_NUMBER_RESULT 89 | ) 90 | 91 | message(STATUS "VERSION_NUMBER: ${VERSION_NUMBER}") 92 | 93 | if(NOT ${VERSION_NUMBER} GREATER_EQUAL 1100) 94 | message(FATAL_ERROR "Wolfram Kernel must be at least version 11.0: ${VERSION_NUMBER}") 95 | endif() 96 | 97 | if(NOT ${VERSION_NUMBER_RESULT} EQUAL "0") 98 | message(WARNING "Bad exit code from VersionNumber script: ${VERSION_NUMBER_RESULT}; Continuing") 99 | endif() 100 | 101 | # 102 | # get $SystemID 103 | # 104 | execute_process( 105 | COMMAND 106 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -runfirst Pause[${KERNEL_PAUSE}]\;Print[OutputForm[$SystemID]]\;Exit[] 107 | OUTPUT_VARIABLE 108 | SYSTEMID 109 | OUTPUT_STRIP_TRAILING_WHITESPACE 110 | WORKING_DIRECTORY 111 | ${PROJECT_SOURCE_DIR} 112 | TIMEOUT 113 | ${KERNEL_TIMEOUT} 114 | RESULT_VARIABLE 115 | SYSTEMID_RESULT 116 | ) 117 | 118 | message(STATUS "SYSTEMID: ${SYSTEMID}") 119 | 120 | if(NOT ${SYSTEMID_RESULT} EQUAL "0") 121 | message(WARNING "Bad exit code from SystemID script: ${SYSTEMID_RESULT}; Continuing") 122 | endif() 123 | 124 | # 125 | # get $SystemWordLength 126 | # 127 | execute_process( 128 | COMMAND 129 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -runfirst Pause[${KERNEL_PAUSE}]\;Print[OutputForm[$SystemWordLength]]\;Exit[] 130 | OUTPUT_VARIABLE 131 | SYSTEMWORDLENGTH 132 | OUTPUT_STRIP_TRAILING_WHITESPACE 133 | WORKING_DIRECTORY 134 | ${PROJECT_SOURCE_DIR} 135 | TIMEOUT 136 | ${KERNEL_TIMEOUT} 137 | RESULT_VARIABLE 138 | SYSTEMWORDLENGTH_RESULT 139 | ) 140 | 141 | message(STATUS "SYSTEMWORDLENGTH: ${SYSTEMWORDLENGTH}") 142 | 143 | if(NOT ${SYSTEMWORDLENGTH_RESULT} EQUAL "0") 144 | message(WARNING "Bad exit code from SystemWordLength script: ${SYSTEMWORDLENGTH_RESULT}; Continuing") 145 | endif() 146 | 147 | # 148 | # Make sure that CMake and Mathematica agree about 32-bit or 64-bit 149 | # 150 | if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "") 151 | # CMAKE_SIZEOF_VOID_P is not set; CXX is probably not enabled 152 | elseif(${CMAKE_SIZEOF_VOID_P} EQUAL 4) 153 | if(NOT ${SYSTEMWORDLENGTH} EQUAL 32) 154 | message(FATAL_ERROR 155 | "CMake is reporting 32-bit; Mathematica is reporting: ${SYSTEMWORDLENGTH}\n" 156 | "HINT: On Windows, you probably need to specify -A x64" 157 | ) 158 | endif() 159 | elseif(${CMAKE_SIZEOF_VOID_P} EQUAL 8) 160 | if(NOT ${SYSTEMWORDLENGTH} EQUAL 64) 161 | message(FATAL_ERROR "CMake is reporting 64-bit; Mathematica is reporting: ${SYSTEMWORDLENGTH}") 162 | endif() 163 | else() 164 | message(FATAL_ERROR "CMake is reporting neither 32-bit nor 64-bit. CMAKE_SIZEOF_VOID_P: ${CMAKE_SIZEOF_VOID_P}") 165 | endif() 166 | 167 | endmacro(CheckWolframKernel) 168 | -------------------------------------------------------------------------------- /Tests/Confirm.mt: -------------------------------------------------------------------------------- 1 | 2 | Needs["CodeInspector`"] 3 | 4 | Needs["CodeParser`"] 5 | 6 | 7 | 8 | 9 | Test[ 10 | CodeInspect["Confirm[expr]"] 11 | , 12 | {InspectionObject["NoSurroundingEnclose", "``Confirm`` has no tag or surrounding ``Enclose``.", "Error", <|Source -> {{1, 1}, {1, 8}}, ConfidenceLevel -> 0.9|>]} 13 | , 14 | TestID->"Confirm-20220211-C5P4I5" 15 | ] 16 | 17 | Test[ 18 | CodeInspect["Confirm[expr, info]"] 19 | , 20 | {InspectionObject["NoSurroundingEnclose", "``Confirm`` has no tag or surrounding ``Enclose``.", "Error", <|Source -> {{1, 1}, {1, 8}}, ConfidenceLevel -> 0.9|>]} 21 | , 22 | TestID->"Confirm-20220211-K3X6R8" 23 | ] 24 | 25 | Test[ 26 | CodeInspect["ConfirmBy[expr, f]"] 27 | , 28 | {InspectionObject["NoSurroundingEnclose", "``ConfirmBy`` has no tag or surrounding ``Enclose``.", "Error", <|Source -> {{1, 1}, {1, 10}}, ConfidenceLevel -> 0.9|>]} 29 | , 30 | TestID->"Confirm-20220211-X2D1H7" 31 | ] 32 | 33 | Test[ 34 | CodeInspect["ConfirmBy[expr, f, info]"] 35 | , 36 | {InspectionObject["NoSurroundingEnclose", "``ConfirmBy`` has no tag or surrounding ``Enclose``.", "Error", <|Source -> {{1, 1}, {1, 10}}, ConfidenceLevel -> 0.9|>]} 37 | , 38 | TestID->"Confirm-20220211-E7M1T9" 39 | ] 40 | 41 | Test[ 42 | CodeInspect["ConfirmMatch[expr, form]"] 43 | , 44 | {InspectionObject["NoSurroundingEnclose", "``ConfirmMatch`` has no tag or surrounding ``Enclose``.", "Error", <|Source -> {{1, 1}, {1, 13}}, ConfidenceLevel -> 0.9|>]} 45 | , 46 | TestID->"Confirm-20220211-T5Q5V2" 47 | ] 48 | 49 | Test[ 50 | CodeInspect["ConfirmMatch[expr, form, info]"] 51 | , 52 | {InspectionObject["NoSurroundingEnclose", "``ConfirmMatch`` has no tag or surrounding ``Enclose``.", "Error", <|Source -> {{1, 1}, {1, 13}}, ConfidenceLevel -> 0.9|>]} 53 | , 54 | TestID->"Confirm-20220211-R5K0P9" 55 | ] 56 | 57 | Test[ 58 | CodeInspect["ConfirmAssert[test]"] 59 | , 60 | {InspectionObject["NoSurroundingEnclose", "``ConfirmAssert`` has no tag or surrounding ``Enclose``.", "Error", <|Source -> {{1, 1}, {1, 14}}, ConfidenceLevel -> 0.9|>]} 61 | , 62 | TestID->"Confirm-20220211-O2E6U9" 63 | ] 64 | 65 | Test[ 66 | CodeInspect["ConfirmAssert[test, info]"] 67 | , 68 | {InspectionObject["NoSurroundingEnclose", "``ConfirmAssert`` has no tag or surrounding ``Enclose``.", "Error", <|Source -> {{1, 1}, {1, 14}}, ConfidenceLevel -> 0.9|>]} 69 | , 70 | TestID->"Confirm-20220211-K1M6T3" 71 | ] 72 | 73 | Test[ 74 | CodeInspect["Enclose[Confirm[expr]]"] 75 | , 76 | {} 77 | , 78 | TestID->"Confirm-20220211-X1O2R2" 79 | ] 80 | 81 | Test[ 82 | CodeInspect["Enclose[Confirm[expr, info]]"] 83 | , 84 | {} 85 | , 86 | TestID->"Confirm-20220211-X6A4X2" 87 | ] 88 | 89 | Test[ 90 | CodeInspect["Enclose[ConfirmBy[expr, f]]"] 91 | , 92 | {} 93 | , 94 | TestID->"Confirm-20220211-O2N4N6" 95 | ] 96 | 97 | Test[ 98 | CodeInspect["Enclose[ConfirmBy[expr, f, info]]"] 99 | , 100 | {} 101 | , 102 | TestID->"Confirm-20220211-K2W8N9" 103 | ] 104 | 105 | Test[ 106 | CodeInspect["Enclose[ConfirmMatch[expr, form]]"] 107 | , 108 | {} 109 | , 110 | TestID->"Confirm-20220211-O6C1J8" 111 | ] 112 | 113 | Test[ 114 | CodeInspect["Enclose[ConfirmMatch[expr, form, info]]"] 115 | , 116 | {} 117 | , 118 | TestID->"Confirm-20220211-K6O8S2" 119 | ] 120 | 121 | Test[ 122 | CodeInspect["Enclose[ConfirmAssert[test]]"] 123 | , 124 | {} 125 | , 126 | TestID->"Confirm-20220211-B5W4N2" 127 | ] 128 | 129 | Test[ 130 | CodeInspect["Enclose[ConfirmAssert[test, info]]"] 131 | , 132 | {} 133 | , 134 | TestID->"Confirm-20220211-E2N1F9" 135 | ] 136 | 137 | Test[ 138 | CodeInspect["Enclose[f[g[h[Confirm[expr]]]]]"] 139 | , 140 | {} 141 | , 142 | TestID->"Confirm-20220211-P5C0D9" 143 | ] 144 | 145 | Test[ 146 | CodeInspect["Enclose[f[g[h[Confirm[expr, info]]]]]"] 147 | , 148 | {} 149 | , 150 | TestID->"Confirm-20220211-P6R7Q6" 151 | ] 152 | 153 | Test[ 154 | CodeInspect["Enclose[f[g[h[ConfirmBy[expr, f]]]]]"] 155 | , 156 | {} 157 | , 158 | TestID->"Confirm-20220211-A9M7R7" 159 | ] 160 | 161 | Test[ 162 | CodeInspect["Enclose[f[g[h[ConfirmBy[expr, f, info]]]]]"] 163 | , 164 | {} 165 | , 166 | TestID->"Confirm-20220211-D5G7I1" 167 | ] 168 | 169 | Test[ 170 | CodeInspect["Enclose[f[g[h[ConfirmMatch[expr, form]]]]]"] 171 | , 172 | {} 173 | , 174 | TestID->"Confirm-20220211-X8H6T4" 175 | ] 176 | 177 | Test[ 178 | CodeInspect["Enclose[f[g[h[ConfirmMatch[expr, form, info]]]]]"] 179 | , 180 | {} 181 | , 182 | TestID->"Confirm-20220211-F2G8Q0" 183 | ] 184 | 185 | Test[ 186 | CodeInspect["Enclose[f[g[h[ConfirmAssert[test]]]]]"] 187 | , 188 | {} 189 | , 190 | TestID->"Confirm-20220211-X3U6C1" 191 | ] 192 | 193 | Test[ 194 | CodeInspect["Enclose[f[g[h[ConfirmAssert[test, info]]]]]"] 195 | , 196 | {} 197 | , 198 | TestID->"Confirm-20220211-G1I1T5" 199 | ] 200 | 201 | 202 | 203 | 204 | 205 | Test[ 206 | CodeInspect["Confirm[expr, info, tag]"] 207 | , 208 | {InspectionObject["NoSurroundingEnclose", "``Confirm`` has no surrounding ``Enclose``.", "Remark", <|Source -> {{1, 1}, {1, 8}}, ConfidenceLevel -> 0.9|>]} 209 | , 210 | TestID->"Confirm-20220211-J5P5R6" 211 | ] 212 | 213 | Test[ 214 | CodeInspect["ConfirmBy[expr, f, info, tag]"] 215 | , 216 | {InspectionObject["NoSurroundingEnclose", "``ConfirmBy`` has no surrounding ``Enclose``.", "Remark", <|Source -> {{1, 1}, {1, 10}}, ConfidenceLevel -> 0.9|>]} 217 | , 218 | TestID->"Confirm-20220211-Q2R8F9" 219 | ] 220 | 221 | Test[ 222 | CodeInspect["ConfirmMatch[expr, form, info, tag]"] 223 | , 224 | {InspectionObject["NoSurroundingEnclose", "``ConfirmMatch`` has no surrounding ``Enclose``.", "Remark", <|Source -> {{1, 1}, {1, 13}}, ConfidenceLevel -> 0.9|>]} 225 | , 226 | TestID->"Confirm-20220211-N7E2L7" 227 | ] 228 | 229 | Test[ 230 | CodeInspect["ConfirmAssert[test, info, tag]"] 231 | , 232 | {InspectionObject["NoSurroundingEnclose", "``ConfirmAssert`` has no surrounding ``Enclose``.", "Remark", <|Source -> {{1, 1}, {1, 14}}, ConfidenceLevel -> 0.9|>]} 233 | , 234 | TestID->"Confirm-20220211-U6T7H9" 235 | ] 236 | 237 | Test[ 238 | CodeInspect["Enclose[Confirm[expr, info, tag]]"] 239 | , 240 | {} 241 | , 242 | TestID->"Confirm-20220211-L4M0N0" 243 | ] 244 | 245 | Test[ 246 | CodeInspect["Enclose[ConfirmBy[expr, f, info, tag]]"] 247 | , 248 | {} 249 | , 250 | TestID->"Confirm-20220211-F1M1D1" 251 | ] 252 | 253 | Test[ 254 | CodeInspect["Enclose[ConfirmMatch[expr, form, info, tag]]"] 255 | , 256 | {} 257 | , 258 | TestID->"Confirm-20220211-H5K2Y1" 259 | ] 260 | 261 | Test[ 262 | CodeInspect["Enclose[ConfirmAssert[test, info, tag]]"] 263 | , 264 | {} 265 | , 266 | TestID->"Confirm-20220211-C0D2O9" 267 | ] 268 | 269 | Test[ 270 | CodeInspect["Enclose[f[g[h[Confirm[expr, info, tag]]]]]"] 271 | , 272 | {} 273 | , 274 | TestID->"Confirm-20220211-A6L0Q6" 275 | ] 276 | 277 | Test[ 278 | CodeInspect["Enclose[f[g[h[ConfirmBy[expr, f, info, tag]]]]]"] 279 | , 280 | {} 281 | , 282 | TestID->"Confirm-20220211-I1G6L5" 283 | ] 284 | 285 | Test[ 286 | CodeInspect["Enclose[f[g[h[ConfirmMatch[expr, form, info, tag]]]]]"] 287 | , 288 | {} 289 | , 290 | TestID->"Confirm-20220211-K5C4I5" 291 | ] 292 | 293 | Test[ 294 | CodeInspect["Enclose[f[g[h[ConfirmAssert[test, info, tag]]]]]"] 295 | , 296 | {} 297 | , 298 | TestID->"Confirm-20220211-X4J3J4" 299 | ] 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | -------------------------------------------------------------------------------- /CodeInspector/Kernel/TokenRules.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["CodeInspector`TokenRules`"] 2 | 3 | $DefaultTokenRules 4 | 5 | 6 | $ScanSessionTokens 7 | 8 | 9 | Begin["`Private`"] 10 | 11 | Needs["CodeInspector`"] 12 | Needs["CodeInspector`Format`"] 13 | Needs["CodeInspector`Utils`"] 14 | Needs["CodeParser`"] 15 | Needs["CodeParser`Utils`"] 16 | 17 | Needs["PacletManager`"] (* for PacletInformation *) 18 | 19 | 20 | 21 | (* 22 | A global to control special handling of session tokens: 23 | 24 | special tokens such as: 25 | % 26 | %% 27 | %45 28 | 29 | and also symbols: 30 | In 31 | Out 32 | etc. 33 | 34 | *) 35 | $ScanSessionTokens = True 36 | 37 | 38 | (* 39 | memoize here because WolframLanguageSyntax`Generate`$badSymbols may not be set yet 40 | *) 41 | badSymbolsStringPat := badSymbolsStringPat = Alternatives @@ WolframLanguageSyntax`Generate`$badSymbols 42 | sessionSymbolsStringPat := sessionSymbolsStringPat = Alternatives @@ WolframLanguageSyntax`Generate`$sessionSymbols 43 | undocumentedSymbolsStringPat := undocumentedSymbolsStringPat = Alternatives @@ WolframLanguageSyntax`Generate`$undocumentedSymbols 44 | 45 | allSymbolsPat := allSymbolsPat = 46 | Alternatives @@ Join[ 47 | WolframLanguageSyntax`Generate`$badSymbols, 48 | WolframLanguageSyntax`Generate`$sessionSymbols, 49 | WolframLanguageSyntax`Generate`$undocumentedSymbols 50 | ] 51 | 52 | 53 | $badSymbolsWithSuggestions = <| 54 | "Failed" -> {0.75, "$Failed"}, 55 | (* low confidence because people do define their own Match *) 56 | "Match" -> {0.5, "MatchQ"}, 57 | "UnSameQ" -> {0.95, "UnsameQ"}, 58 | (* low confidence because people do define their own StringMatch *) 59 | "StringMatch" -> {0.5, "StringMatchQ"}, 60 | "OptionsQ" -> {0.95, "OptionQ"}, 61 | "OptionPattern" -> {0.95, "OptionsPattern"}, 62 | "InterpolationFunction" -> {0.95, "InterpolatingFunction"}, 63 | "InterpolationPolynomial" -> {0.95, "InterpolatingPolynomial"}, 64 | (* low confidence because people do define their own RealQ *) 65 | "RealQ" -> {0.5, "Developer`RealQ"}, 66 | (* low confidence because people do define their own SymbolQ *) 67 | "SymbolQ" -> {0.5, "Developer`SymbolQ"}, 68 | "DataSet" -> {0.95, "Dataset"}, 69 | "UrlExecute" -> {0.95, "URLExecute"}, 70 | "Cloudbase" -> {0.95, "CloudBase"}, 71 | "ExpandFilename" -> {0.95, "ExpandFileName"}, 72 | "$PathNameSeparator" -> {0.95, "$PathnameSeparator"}, 73 | "$RegisteredUsername" -> {0.95, "$RegisteredUserName"}, 74 | (* 75 | technically, ExportPacket IS in System`, but should stop using it 76 | https://mail-archive.wolfram.com/archive/l-frontend/2022/Oct00/0002.html 77 | *) 78 | "ExportPacket" -> {0.95, "FrontEnd`ExportPacket"} 79 | |> 80 | 81 | $badSymbolsNoSuggestions = <| 82 | (* 83 | low confidence because people do define their own Boolean 84 | could suggest True|False 85 | *) 86 | "Boolean" -> {0.5}, 87 | (* low confidence because people do define their own FalseQ *) 88 | "FalseQ" -> {0.5}, 89 | (* low confidence because people do define their own RationalQ *) 90 | "RationalQ" -> {0.5}, 91 | (* low confidence because people do define their own ComplexQ *) 92 | "ComplexQ" -> {0.5} 93 | |> 94 | 95 | $undocumentedSymbolsWithSuggestions = <| 96 | "$UserName" -> {0.95, "$Username"} 97 | |> 98 | 99 | 100 | (* 101 | memoize because allSymbolsPat depends on WolframLanguageSyntax`Generate`$badSymbols which may not be set yet 102 | *) 103 | $DefaultTokenRules := $DefaultTokenRules = <| 104 | 105 | LeafNode[Symbol, allSymbolsPat, _] -> scanSymbols, 106 | 107 | LeafNode[Token`Percent | Token`PercentPercent, _, _] | CompoundNode[Out, _, _] /; $ScanSessionTokens -> scanSessionTokens, 108 | 109 | Nothing 110 | |> 111 | 112 | 113 | 114 | Attributes[scanSymbols] = {HoldRest} 115 | 116 | scanSymbols[pos_List, cstIn_] := 117 | Module[{cst, node, data, str, issues, src, withSuggestion, 118 | noSuggestion}, 119 | cst = cstIn; 120 | node = Extract[cst, {pos}][[1]]; 121 | str = node[[2]]; 122 | data = node[[3]]; 123 | src = data[Source]; 124 | 125 | issues = {}; 126 | 127 | Which[ 128 | StringMatchQ[str, badSymbolsStringPat], 129 | Which[ 130 | !MissingQ[(withSuggestion = $badSymbolsWithSuggestions[str])], 131 | AppendTo[issues, 132 | InspectionObject["BadSymbol", "``" <> str <> "`` does not exist in **System`** context.", "Error", 133 | <| 134 | Source -> src, 135 | ConfidenceLevel -> withSuggestion[[1]], 136 | CodeActions -> { 137 | CodeAction["Replace with ``" <> withSuggestion[[2]] <> "``", ReplaceNode, <| 138 | Source -> src, 139 | "ReplacementNode" -> LeafNode[Symbol, withSuggestion[[2]], <||>] 140 | |>] 141 | }, 142 | "Argument" -> str 143 | |> 144 | ] 145 | ] 146 | , 147 | !MissingQ[(noSuggestion = $badSymbolsNoSuggestions[str])], 148 | AppendTo[issues, 149 | InspectionObject["BadSymbol", "``" <> str <> "`` does not exist in **System`** context.", "Error", 150 | <| 151 | Source -> src, 152 | ConfidenceLevel -> withSuggestion[[1]], 153 | "Argument" -> str 154 | |> 155 | ] 156 | ] 157 | , 158 | True, 159 | (* everything else without suggestions or custom ConfidenceLevel*) 160 | AppendTo[issues, 161 | InspectionObject["BadSymbol", "``" <> str <> "`` does not exist in **System`** context.", "Error", 162 | <| 163 | Source -> src, 164 | ConfidenceLevel -> 0.75, 165 | "Argument" -> str 166 | |> 167 | ] 168 | ] 169 | ] 170 | , 171 | $ScanSessionTokens && StringMatchQ[str, sessionSymbolsStringPat], 172 | AppendTo[issues, 173 | InspectionObject["SuspiciousSessionSymbol", "Suspicious use of session symbol " <> format[str] <> ".", "Warning", 174 | <| 175 | Source -> src, 176 | ConfidenceLevel -> 0.55 177 | |> 178 | ] 179 | ] 180 | , 181 | StringMatchQ[str, undocumentedSymbolsStringPat], 182 | Which[ 183 | !MissingQ[(withSuggestion = $undocumentedSymbolsWithSuggestions[str])], 184 | AppendTo[issues, 185 | InspectionObject["UndocumentedSymbol", "``" <> str <> "`` is undocumented.", "Remark", 186 | <| 187 | Source -> src, 188 | ConfidenceLevel -> withSuggestion[[1]], 189 | CodeActions -> { 190 | CodeAction["Replace with ``" <> withSuggestion[[2]] <> "``", ReplaceNode, <| 191 | Source -> src, 192 | "ReplacementNode" -> LeafNode[Symbol, withSuggestion[[2]], <||>] 193 | |>] 194 | }, 195 | "Argument" -> str 196 | |> 197 | ] 198 | ] 199 | , 200 | True, 201 | (* 202 | everything else without suggestions or custom ConfidenceLevel 203 | 204 | do not give any lints here 205 | 206 | undocumented symbols are generally ok 207 | *) 208 | Null 209 | ] 210 | , 211 | True, 212 | Null 213 | ]; 214 | 215 | issues 216 | ] 217 | 218 | 219 | 220 | Attributes[scanSessionTokens] = {HoldRest} 221 | 222 | scanSessionTokens[pos_List, cstIn_] := 223 | Module[{cst, node, data, str, issues, src}, 224 | cst = cstIn; 225 | node = Extract[cst, {pos}][[1]]; 226 | str = node[[2]]; 227 | data = node[[3]]; 228 | src = data[Source]; 229 | 230 | issues = {}; 231 | 232 | AppendTo[issues, 233 | InspectionObject["SuspiciousSessionToken", "Suspicious use of session token.", "Error", 234 | <| 235 | Source -> src, 236 | ConfidenceLevel -> 0.95 237 | |> 238 | ] 239 | ]; 240 | 241 | issues 242 | ] 243 | 244 | 245 | End[] 246 | 247 | 248 | EndPackage[] 249 | -------------------------------------------------------------------------------- /CodeInspector/Kernel/BracketMismatches.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["CodeInspector`BracketMismatches`"] 2 | 3 | CodeInspectBracketMismatches 4 | 5 | CodeInspectBracketMismatchesSummarize 6 | 7 | 8 | CodeInspectBracketMismatchesCST 9 | 10 | CodeInspectBracketMismatchesCSTSummarize 11 | 12 | 13 | CodeInspectBracketMismatchesAgg 14 | 15 | 16 | Begin["`Private`"] 17 | 18 | Needs["CodeInspector`"] 19 | Needs["CodeInspector`Summarize`"] 20 | Needs["CodeInspector`Format`"] 21 | Needs["CodeInspector`Utils`"] 22 | Needs["CodeParser`"] 23 | Needs["CodeParser`Abstract`"] 24 | Needs["CodeParser`Utils`"] 25 | 26 | 27 | 28 | (* 29 | How many bracket mismatches to keep? 30 | *) 31 | $BracketMismatchesLimit = 1 32 | 33 | 34 | 35 | CodeInspectBracketMismatches::usage = "CodeInspectBracketMismatches[code] returns a list of bracket mismatches in code." 36 | 37 | Options[CodeInspectBracketMismatches] = { 38 | PerformanceGoal -> "Speed", 39 | (* 40 | Pass through to CodeConcreteParse 41 | *) 42 | CharacterEncoding -> "UTF-8", 43 | SourceConvention -> "LineColumn", 44 | "TabWidth" -> 1, 45 | "FileFormat" -> Automatic 46 | } 47 | 48 | 49 | $fileByteCountMinLimit = 0*^6 50 | $fileByteCountMaxLimit = 3*^6 51 | 52 | 53 | 54 | CodeInspectBracketMismatches[File[file_String], opts:OptionsPattern[]] := 55 | Catch[ 56 | Module[{full, performanceGoal, cst}, 57 | 58 | performanceGoal = OptionValue[PerformanceGoal]; 59 | 60 | full = FindFile[file]; 61 | If[FailureQ[full], 62 | Throw[Failure["FindFileFailed", <| "FileName" -> file |>]] 63 | ]; 64 | 65 | If[performanceGoal == "Speed", 66 | If[FileByteCount[full] > $fileByteCountMaxLimit, 67 | Throw[Failure["FileTooLarge", <| "FileName" -> full, "FileSize" -> FileSize[full] |>]] 68 | ]; 69 | If[FileByteCount[full] < $fileByteCountMinLimit, 70 | Throw[Failure["FileTooSmall", <| "FileName" -> full, "FileSize" -> FileSize[full] |>]] 71 | ]; 72 | ]; 73 | 74 | cst = CodeConcreteParse[File[full], FilterRules[{opts}, Options[CodeConcreteParse]]]; 75 | 76 | CodeInspectBracketMismatchesCST[cst, FilterRules[{opts}, Options[CodeInspectBracketMismatchesCST]]] 77 | ]] 78 | 79 | 80 | 81 | 82 | 83 | CodeInspectBracketMismatches[string_String, opts:OptionsPattern[]] := 84 | Catch[ 85 | Module[{cst}, 86 | 87 | cst = CodeConcreteParse[string, FilterRules[{opts}, Options[CodeConcreteParse]]]; 88 | 89 | CodeInspectBracketMismatchesCST[cst, FilterRules[{opts}, Options[CodeInspectBracketMismatchesCST]]] 90 | ]] 91 | 92 | 93 | 94 | 95 | CodeInspectBracketMismatchesCST[cst_] := 96 | Catch[ 97 | Module[{mismatches, agg}, 98 | 99 | If[FailureQ[cst], 100 | Throw[cst] 101 | ]; 102 | 103 | agg = Aggregate[cst]; 104 | 105 | mismatches = bracketMismatches[agg]; 106 | 107 | mismatches 108 | ]] 109 | 110 | CodeInspectBracketMismatchesAgg[agg_] := 111 | Catch[ 112 | Module[{mismatches}, 113 | 114 | If[FailureQ[agg], 115 | Throw[agg] 116 | ]; 117 | 118 | mismatches = bracketMismatches[agg]; 119 | 120 | mismatches 121 | ]] 122 | 123 | 124 | 125 | 126 | CodeInspectBracketMismatchesSummarize::usage = "BracketMismatchSummarize[code] returns an inspection summary object." 127 | 128 | Options[CodeInspectBracketMismatchesSummarize] = { 129 | (* 130 | pass through to CodeInspect 131 | *) 132 | CharacterEncoding -> "UTF-8", 133 | SourceConvention -> "LineColumn", 134 | "TabWidth" -> 1, 135 | "FileFormat" -> Automatic 136 | } 137 | 138 | CodeInspectBracketMismatchesSummarize[File[file_String], bracketMismatchesIn:{(GroupMissingCloserNode|UnterminatedGroupNode|ErrorNode)[_, _, _]...}:Automatic, opts:OptionsPattern[]] := 139 | Catch[ 140 | Module[{mismatches, full, lines, lintedLines, bytes, str, tabWidth}, 141 | 142 | mismatches = bracketMismatchesIn; 143 | 144 | tabWidth = OptionValue["TabWidth"]; 145 | 146 | full = FindFile[file]; 147 | If[FailureQ[full], 148 | Throw[Failure["FindFileFailed", <| "FileName" -> file |>]] 149 | ]; 150 | 151 | If[FileByteCount[full] == 0, 152 | Throw[Failure["EmptyFile", <| "FileName" -> full |>]] 153 | ]; 154 | 155 | If[mismatches === Automatic, 156 | mismatches = CodeInspectBracketMismatches[File[full], FilterRules[{opts}, Options[CodeInspectBracketMismatches]]]; 157 | ]; 158 | 159 | (* 160 | Was: 161 | bytes = Import[full, "Byte"]; 162 | 163 | but this is slow 164 | *) 165 | bytes = ReadByteArray[full]; 166 | 167 | str = SafeString[bytes]; 168 | 169 | If[FailureQ[str], 170 | Throw[str] 171 | ]; 172 | 173 | If[MissingQ[str], 174 | Throw[str] 175 | ]; 176 | 177 | lines = StringSplit[str, {"\r\n", "\n", "\r"}, All]; 178 | 179 | lines = replaceTabs[#, 1, "!", tabWidth]& /@ lines; 180 | 181 | lintedLines = bracketMismatchesLinesReport[lines, mismatches]; 182 | InspectedFileObject[full, lintedLines] 183 | ]] 184 | 185 | 186 | 187 | 188 | 189 | CodeInspectBracketMismatchesSummarize[string_String, bracketMismatchesIn:{(GroupMissingCloserNode|UnterminatedGroupNode|ErrorNode)[_, _, _]...}:Automatic, opts:OptionsPattern[]] := 190 | Catch[ 191 | Module[{mismatches, lines, lintedLines, tabWidth}, 192 | 193 | mismatches = bracketMismatchesIn; 194 | 195 | tabWidth = OptionValue["TabWidth"]; 196 | 197 | If[StringLength[string] == 0, 198 | Throw[Failure["EmptyString", <||>]] 199 | ]; 200 | 201 | If[mismatches === Automatic, 202 | mismatches = CodeInspectBracketMismatches[string, FilterRules[{opts}, Options[CodeInspectBracketMismatches]]]; 203 | ]; 204 | 205 | lines = StringSplit[string, {"\r\n", "\n", "\r"}, All]; 206 | 207 | lines = replaceTabs[#, 1, "!", tabWidth]& /@ lines; 208 | 209 | lintedLines = bracketMismatchesLinesReport[lines, mismatches]; 210 | InspectedStringObject[string, lintedLines] 211 | ]] 212 | 213 | 214 | 215 | 216 | Options[CodeInspectBracketMismatchesCSTSummarize] = { 217 | "TabWidth" -> 1 218 | } 219 | 220 | (* 221 | precondition: 222 | Source convention for implicitTokens is "LineColumn" 223 | 224 | *) 225 | CodeInspectBracketMismatchesCSTSummarize[cst_, bracketMismatchesIn:{(GroupMissingCloserNode|UnterminatedGroupNode|ErrorNode)[_, _, _]...}:Automatic, opts:OptionsPattern[]] := 226 | Catch[ 227 | Module[{mismatches, lines, lintedLines, string, tabWidth}, 228 | 229 | If[FailureQ[cst], 230 | Throw[cst] 231 | ]; 232 | 233 | mismatches = bracketMismatchesIn; 234 | 235 | tabWidth = OptionValue["TabWidth"]; 236 | 237 | If[mismatches === Automatic, 238 | mismatches = CodeInspectBracketMismatchesCST[cst, FilterRules[{opts}, Options[CodeInspectBracketMismatchesCST]]]; 239 | ]; 240 | 241 | string = ToSourceCharacterString[cst]; 242 | 243 | lines = StringSplit[string, {"\r\n", "\n", "\r"}, All]; 244 | 245 | lines = replaceTabs[#, 1, "!", tabWidth]& /@ lines; 246 | 247 | lintedLines = bracketMismatchesLinesReport[lines, mismatches]; 248 | InspectedStringObject[string, lintedLines] 249 | ]] 250 | 251 | 252 | 253 | bracketMismatches[agg_] := 254 | Catch[ 255 | Module[{mismatches}, 256 | 257 | mismatches = Cases[agg, GroupMissingCloserNode[_, _, _] | UnterminatedGroupNode[_, _, _] | ErrorNode[Token`Error`UnexpectedCloser, _, _], {0, Infinity}]; 258 | 259 | mismatches 260 | ]] 261 | 262 | 263 | 264 | (* how many (, ), or \[Times] to insert per line *) 265 | $markupLimit = 100 266 | 267 | 268 | $color = severityColor[{ 269 | InspectionObject["GroupMissingCloser", "Missing closer.", "Fatal", <||>], 270 | InspectionObject["UnexpectedCloser", "Unexpected closer.", "Fatal", <||>]}]; 271 | 272 | 273 | (* 274 | Return list of characters representing the under line 275 | *) 276 | modify[lineIn_String, {missingOpenerStarts_, missingCloserStarts_}, lineNumber_] := 277 | Module[{line, infixCols, infixInserters, under, 278 | rules, missingOpenerCols, missingCloserCols, missingOpenerInserters, missingCloserInserters}, 279 | 280 | line = lineIn; 281 | 282 | missingOpenerCols = Cases[missingOpenerStarts, {lineNumber, col_} :> col]; 283 | missingCloserCols = Cases[missingCloserStarts, {lineNumber, col_} :> col]; 284 | 285 | missingOpenerInserters = AssociationMap[LintMarkup[LintMissingOpenerIndicatorCharacter, FontWeight->CodeInspector`Format`$LintGridFontWeight, FontColor->$color]&, missingOpenerCols]; 286 | missingCloserInserters = AssociationMap[LintMarkup[LintMissingCloserIndicatorCharacter, FontWeight->CodeInspector`Format`$LintGridFontWeight, FontColor->$color]&, missingCloserCols]; 287 | 288 | If[$Debug, 289 | Print["lineNumber: ", lineNumber]; 290 | Print["infixInserters: ", infixInserters]; 291 | ]; 292 | 293 | rules = Join[missingOpenerInserters, missingCloserInserters]; 294 | rules = Normal[rules]; 295 | 296 | under = Table[" ", {StringLength[line]}]; 297 | 298 | (* 299 | extend line to be able to insert \[Times] after the line, when ImplicitTimes spans lines 300 | *) 301 | under = Join[under, {" "}]; 302 | 303 | under = ReplacePart[under, rules]; 304 | 305 | (* 306 | to match Listify 307 | *) 308 | under = Join[{" "}, under]; 309 | 310 | under 311 | ] 312 | 313 | 314 | (* 315 | precondition: 316 | Source convention for implicitTokens is "LineColumn" 317 | 318 | *) 319 | bracketMismatchesLinesReport[linesIn:{___String}, bracketMismatchesIn:{(GroupMissingCloserNode|UnterminatedGroupNode|ErrorNode)[_, _, _]...}] := 320 | Catch[ 321 | Module[{mismatches, infixs, lines, linesToModify, missingOpeners, missingClosers, missingOpenerStarts, 322 | missingCloserStarts, maxLineNumberLength}, 323 | 324 | If[bracketMismatchesIn === {}, 325 | Throw[{}] 326 | ]; 327 | 328 | mismatches = bracketMismatchesIn; 329 | 330 | lines = linesIn; 331 | 332 | If[$Debug, 333 | Print["lines: ", lines]; 334 | ]; 335 | 336 | mismatches = Take[mismatches, UpTo[$BracketMismatchesLimit]]; 337 | 338 | missingOpeners = Cases[mismatches, ErrorNode[Token`Error`UnexpectedCloser, _, _]]; 339 | 340 | missingClosers = Cases[mismatches, GroupMissingCloserNode[_, _, _] | UnterminatedGroupNode[_, _, _]]; 341 | 342 | missingOpenerStarts = missingOpeners[[All, 3, Key[Source], 1]]; 343 | 344 | missingCloserStarts = missingClosers[[All, 3, Key[Source], 1]]; 345 | 346 | linesToModify = Union[missingOpenerStarts[[All, 1]], missingCloserStarts[[All, 1]]]; 347 | 348 | maxLineNumberLength = Max[IntegerLength /@ linesToModify]; 349 | 350 | Table[ 351 | 352 | InspectedLineObject[lines[[i]], i, {ListifyLine[lines[[i]], <||>, "EndOfFile" -> (i == Length[lines])], 353 | modify[lines[[i]], {missingOpenerStarts, missingCloserStarts}, i]}, 354 | {}, "MaxLineNumberLength" -> maxLineNumberLength] 355 | , 356 | {i, linesToModify} 357 | ] 358 | ]] 359 | 360 | 361 | 362 | End[] 363 | 364 | EndPackage[] 365 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # https://cmake.org/cmake/help/latest/release/3.15.html 4 | # The cmake(1) command gained a new --install option. This may be used after building a project to run installation without using the generated build system or the native build tool. 5 | # 6 | cmake_minimum_required(VERSION 3.15) 7 | 8 | project(codeinspector 9 | LANGUAGES 10 | NONE 11 | ) 12 | 13 | set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) 14 | include(WolframKernel) 15 | include(PacletInfo) 16 | 17 | # 18 | # Used for quickly reporting syntax errors in WL source files 19 | # 20 | find_program(CODEPARSER_EXE 21 | NAMES 22 | codeparser codeparser.exe 23 | HINTS 24 | ${CODEPARSER_EXE_DIR} 25 | ) 26 | 27 | set(PACLET "CodeInspector") 28 | set(PACLET_LAYOUT_DIR "paclet" CACHE FILEPATH "Path to complete, built paclet layout (relative to build directory)") 29 | set(WOLFRAMKERNEL ${WOLFRAMKERNEL_DEFAULT} CACHE FILEPATH "Path to WolframKernel") 30 | set(LOCAL_BUILD OFF CACHE BOOL "Local build") 31 | # 32 | # Time to pause when starting kernel 33 | # 34 | # Work-around for bug 349779 is to pause ~1 second 35 | # bug 349779 was fixed in version 12.0 36 | # 37 | # Related bugs: 349779 38 | # 39 | set(KERNEL_PAUSE 0 CACHE STRING "Kernel startup pause") 40 | # 41 | # Timeout for starting a kernel and getting a result 42 | # 43 | # RE machines can be very slow when starting a kernel, so we need to be very generous with this timeout 44 | # 45 | # Should be at least 10 minutes = 600 seconds 46 | # 47 | # Evidence suggests that when bug 349779 strikes, the kernel does exit after 30 minutes = 1800 seconds 48 | # bug 349779 was fixed in version 12.0 49 | # 50 | # Related bugs: 349779 51 | # Related issues: RE-514227 52 | # 53 | set(KERNEL_TIMEOUT 600 CACHE STRING "Kernel startup timeout") 54 | 55 | if(NOT DEFINED BUILDNUMBER) 56 | set(BUILDNUMBER 0) 57 | endif() 58 | message(STATUS "BUILDNUMBER: ${BUILDNUMBER}") 59 | message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}") 60 | message(STATUS "PACLET: ${PACLET}") 61 | message(STATUS "PACLET_LAYOUT_DIR: ${PACLET_LAYOUT_DIR}") 62 | message(STATUS "WOLFRAMKERNEL: ${WOLFRAMKERNEL}") 63 | message(STATUS "LOCAL_BUILD: ${LOCAL_BUILD}") 64 | if(LOCAL_BUILD) 65 | message(STATUS "Configuring for local build") 66 | endif() 67 | # message(STATUS "CMAKE_SIZEOF_VOID_P: ${CMAKE_SIZEOF_VOID_P}") 68 | message(STATUS "KERNEL_PAUSE: ${KERNEL_PAUSE}") 69 | message(STATUS "KERNEL_TIMEOUT: ${KERNEL_TIMEOUT}") 70 | message(STATUS "CODEPARSER_EXE: ${CODEPARSER_EXE}") 71 | if(NOT CODEPARSER_EXE) 72 | message(STATUS "Optional tool CODEPARSER_EXE was not found; skipping") 73 | endif() 74 | 75 | set(STATIC_WL_PACLET_KERNEL_SOURCES 76 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/AbstractRules.wl 77 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/AggregateRules.wl 78 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/Boxes.wl 79 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/BracketMismatches.wl 80 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/CodeInspector.wl 81 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/ConcreteRules.wl 82 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/External.wl 83 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/Format.wl 84 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/ImplicitTokens.wl 85 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/LinterUI.wl 86 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/MessageStack.wl 87 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/Summarize.wl 88 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/SuppressedRegions.wl 89 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/TokenRules.wl 90 | ${PROJECT_SOURCE_DIR}/CodeInspector/Kernel/Utils.wl 91 | ) 92 | 93 | set(STATIC_WL_PACLET_FRONTEND_SOURCES 94 | ${PROJECT_SOURCE_DIR}/CodeInspector/FrontEnd/TextResources/CodeInspector.tr 95 | ) 96 | 97 | set(PROCESSED_DATA_FILES 98 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/AnalyzableMessagePositions.wl 99 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/AnalyzableMessages.wl 100 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/BadSymbols.wl 101 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/BuiltinFunctions.wl 102 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/Constants.wl 103 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/ExperimentalSymbols.wl 104 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/FreeCharacters.wl 105 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/FreeLongNames.wl 106 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/Options.wl 107 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/ObsoleteSymbols.wl 108 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/SessionSymbols.wl 109 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/SpecialCharacters.wl 110 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/SpecialLongNames.wl 111 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/SystemCharacters.wl 112 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/SystemLongNames.wl 113 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/UndocumentedCharacters.wl 114 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/UndocumentedLongNames.wl 115 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/UndocumentedSymbols.wl 116 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/UnsupportedCharacters.wl 117 | ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/UnsupportedLongNames.wl 118 | ) 119 | 120 | set(STATIC_WL_PACLET_RESOURCES_SOURCES 121 | ${PROJECT_SOURCE_DIR}/CodeInspector/Resources/Examples/Collatz.m 122 | ) 123 | 124 | set(PACLETINFO_IN_SOURCE 125 | ${PROJECT_SOURCE_DIR}/${PACLET}/PacletInfo.wl.in 126 | ) 127 | 128 | set(GENERATED_WL_PACLET_FRONTEND_SOURCES 129 | ${PROJECT_BINARY_DIR}/paclet/CodeInspector/FrontEnd/Palettes/CodeAnalysisOptions.nb 130 | ) 131 | 132 | 133 | # 134 | # Set VERSION_NUMBER, SYSTEMID, and PACLET_VERSION 135 | # 136 | CheckWolframKernel() 137 | CheckPacletInfo() 138 | 139 | # 140 | # Force re-configure if PacletInfo.wl.in changes, e.g. paclet version is changed and name of .paclet has changed 141 | # 142 | set_property( 143 | DIRECTORY 144 | APPEND 145 | PROPERTY 146 | CMAKE_CONFIGURE_DEPENDS 147 | ${PACLETINFO_IN_SOURCE} 148 | ) 149 | 150 | 151 | if(NOT VERSION_NUMBER GREATER_EQUAL 1210) 152 | message(WARNING "VERSION_NUMBER is below 1210; ForceVersionInstall was added in 12.1. Installing paclets via CMake may not work. (VERSION_NUMBER is ${VERSION_NUMBER})") 153 | endif() 154 | 155 | 156 | file(MAKE_DIRECTORY 157 | ${PROJECT_BINARY_DIR}/paclet/${PACLET} 158 | ${PROJECT_BINARY_DIR}/paclet/${PACLET}/FrontEnd/Palettes/ 159 | ) 160 | 161 | 162 | # 163 | # Copy WL source files 164 | # 165 | 166 | set(REPLACED_PACLETINFO ${PROJECT_BINARY_DIR}/paclet/${PACLET}/PacletInfo.wl) 167 | 168 | add_custom_command( 169 | OUTPUT 170 | ${REPLACED_PACLETINFO} 171 | COMMAND 172 | ${CMAKE_COMMAND} -DSRC=${PACLETINFO_IN_SOURCE} -DCODEPARSER_EXE=${CODEPARSER_EXE} -DWOLFRAMKERNEL=${WOLFRAMKERNEL} -DKERNEL_TIMEOUT=${KERNEL_TIMEOUT} -P ${PROJECT_SOURCE_DIR}/cmake/InspectFile.cmake 173 | COMMAND 174 | ${CMAKE_COMMAND} -DTRANSPORT=${TRANSPORT} -DBUILDNUMBER=${BUILDNUMBER} -DVERSION_NUMBER=${VERSION_NUMBER} -DWOLFRAMLIBRARY_VERSION=${WOLFRAMLIBRARY_VERSION} -DLOCAL_BUILD=${LOCAL_BUILD} -DLOCAL_BUILD_VERSION=${LOCAL_BUILD_VERSION} -DPACLETINFO_IN_SOURCE=${PACLETINFO_IN_SOURCE} -DREPLACED_PACLETINFO=${REPLACED_PACLETINFO} -P ${PROJECT_SOURCE_DIR}/cmake/ReplacePacletInfo.cmake 175 | DEPENDS 176 | ${PACLETINFO_IN_SOURCE} 177 | ${PROJECT_SOURCE_DIR}/cmake/InspectFile.cmake 178 | ${PROJECT_SOURCE_DIR}/cmake/ReplacePacletInfo.cmake 179 | ) 180 | 181 | 182 | # 183 | # static Kernel sources 184 | # 185 | foreach(SRC ${STATIC_WL_PACLET_KERNEL_SOURCES}) 186 | file(RELATIVE_PATH REL ${PROJECT_SOURCE_DIR}/${PACLET}/Kernel/ ${SRC}) 187 | add_custom_command( 188 | OUTPUT 189 | ${PROJECT_BINARY_DIR}/paclet/${PACLET}/Kernel/${REL} 190 | COMMAND 191 | ${CMAKE_COMMAND} -DSRC=${SRC} -DCODEPARSER_EXE=${CODEPARSER_EXE} -DWOLFRAMKERNEL=${WOLFRAMKERNEL} -DKERNEL_TIMEOUT=${KERNEL_TIMEOUT} -P ${PROJECT_SOURCE_DIR}/cmake/InspectFile.cmake 192 | COMMAND 193 | ${CMAKE_COMMAND} -E copy ${SRC} ${PROJECT_BINARY_DIR}/paclet/${PACLET}/Kernel/${REL} 194 | DEPENDS 195 | ${SRC} 196 | ${PROJECT_SOURCE_DIR}/cmake/InspectFile.cmake 197 | ) 198 | list(APPEND COPIED_WL_PACLET_SOURCES ${PROJECT_BINARY_DIR}/paclet/${PACLET}/Kernel/${REL}) 199 | endforeach() 200 | 201 | # 202 | # static FrontEnd sources 203 | # 204 | foreach(SRC ${STATIC_WL_PACLET_FRONTEND_SOURCES}) 205 | file(RELATIVE_PATH REL ${PROJECT_SOURCE_DIR}/${PACLET}/FrontEnd/ ${SRC}) 206 | add_custom_command( 207 | OUTPUT 208 | ${PROJECT_BINARY_DIR}/paclet/${PACLET}/FrontEnd/${REL} 209 | # 210 | # Do not inspect FrontEnd sources 211 | # 212 | COMMAND 213 | ${CMAKE_COMMAND} -E copy ${SRC} ${PROJECT_BINARY_DIR}/paclet/${PACLET}/FrontEnd/${REL} 214 | DEPENDS 215 | ${SRC} 216 | ) 217 | list(APPEND COPIED_WL_PACLET_SOURCES ${PROJECT_BINARY_DIR}/paclet/${PACLET}/FrontEnd/${REL}) 218 | endforeach() 219 | 220 | # 221 | # processed Data sources 222 | # 223 | foreach(SRC ${PROCESSED_DATA_FILES}) 224 | file(RELATIVE_PATH REL ${PROJECT_SOURCE_DIR}/WolframLanguageSyntax/Data/ ${SRC}) 225 | add_custom_command( 226 | OUTPUT 227 | ${PROJECT_BINARY_DIR}/paclet/${PACLET}/Resources/Data/${REL} 228 | COMMAND 229 | ${CMAKE_COMMAND} -E copy ${SRC} ${PROJECT_BINARY_DIR}/paclet/${PACLET}/Resources/Data/${REL} 230 | DEPENDS 231 | ${SRC} 232 | ) 233 | list(APPEND COPIED_WL_PACLET_SOURCES ${PROJECT_BINARY_DIR}/paclet/${PACLET}/Resources/Data/${REL}) 234 | endforeach() 235 | 236 | # 237 | # static Resources sources 238 | # 239 | foreach(SRC ${STATIC_WL_PACLET_RESOURCES_SOURCES}) 240 | file(RELATIVE_PATH REL ${PROJECT_SOURCE_DIR}/${PACLET}/Resources/ ${SRC}) 241 | add_custom_command( 242 | OUTPUT 243 | ${PROJECT_BINARY_DIR}/paclet/${PACLET}/Resources/${REL} 244 | # 245 | # Do not inspect Resources sources 246 | # 247 | COMMAND 248 | ${CMAKE_COMMAND} -E copy ${SRC} ${PROJECT_BINARY_DIR}/paclet/${PACLET}/Resources/${REL} 249 | DEPENDS 250 | ${SRC} 251 | ) 252 | list(APPEND COPIED_WL_PACLET_SOURCES ${PROJECT_BINARY_DIR}/paclet/${PACLET}/Resources/${REL}) 253 | endforeach() 254 | 255 | 256 | # 257 | # generated srcs 258 | # 259 | 260 | # 261 | # CodeAnalysisOptions.nb 262 | # 263 | add_custom_command( 264 | OUTPUT 265 | ${PROJECT_BINARY_DIR}/paclet/CodeInspector/FrontEnd/Palettes/CodeAnalysisOptions.nb 266 | COMMAND 267 | ${CMAKE_COMMAND} -DSCRIPT=${PROJECT_SOURCE_DIR}/CodeInspector/Generate/MakeCodeAnalysisOptionsPalette.wl -DSRCDIR=${PROJECT_SOURCE_DIR} -DBUILDDIR=${PROJECT_BINARY_DIR} -DWOLFRAMKERNEL=${WOLFRAMKERNEL} -DKERNEL_TIMEOUT=${KERNEL_TIMEOUT} -P ${PROJECT_SOURCE_DIR}/cmake/WolframScript.cmake 268 | DEPENDS 269 | ${PROJECT_SOURCE_DIR}/CodeInspector/Generate/MakeCodeAnalysisOptionsPalette.wl 270 | ${PROJECT_SOURCE_DIR}/CodeTools/Generate/GenerateSources.wl 271 | ${PROJECT_SOURCE_DIR}/cmake/WolframScript.cmake 272 | VERBATIM 273 | WORKING_DIRECTORY 274 | ${PROJECT_SOURCE_DIR} 275 | ) 276 | 277 | 278 | # 279 | # paclet layout 280 | # 281 | 282 | set(PACLET_SOURCES 283 | ${REPLACED_PACLETINFO} 284 | ${COPIED_WL_PACLET_SOURCES} 285 | ${GENERATED_WL_PACLET_FRONTEND_SOURCES} 286 | ) 287 | 288 | 289 | 290 | # 291 | # paclet archive 292 | # 293 | 294 | if(LOCAL_BUILD) 295 | set(PACLET_OUTPUT ${PROJECT_BINARY_DIR}/paclet/${PACLET}-${LOCAL_BUILD_VERSION}.paclet) 296 | else(LOCAL_BUILD) 297 | set(PACLET_OUTPUT ${PROJECT_BINARY_DIR}/paclet/${PACLET}-${PACLET_VERSION}.paclet) 298 | endif(LOCAL_BUILD) 299 | 300 | add_custom_target(create-paclet-archive 301 | ALL 302 | DEPENDS 303 | ${PACLET_OUTPUT} 304 | ) 305 | 306 | # 307 | # CreatePacletArchive 308 | # 309 | add_custom_command( 310 | OUTPUT 311 | ${PACLET_OUTPUT} 312 | COMMAND 313 | # 314 | # CreatePacletArchive may be slow on RE machines, so allow re-trying if JLink connection timeout is hit 315 | # 316 | # see: RE-515885 317 | # 318 | ${CMAKE_COMMAND} -DRETRY_ON_FAILURE=ON -DSCRIPT=${PROJECT_SOURCE_DIR}/CodeTools/Generate/CreatePacletArchive.wl -DBUILDDIR=${PROJECT_BINARY_DIR} -DPACLET_LAYOUT_DIR=${PACLET_LAYOUT_DIR} -DPACLET=${PACLET} -DKERNEL_TIMEOUT=${KERNEL_TIMEOUT} -DWOLFRAMKERNEL=${WOLFRAMKERNEL} -P ${PROJECT_SOURCE_DIR}/cmake/WolframScript.cmake 319 | DEPENDS 320 | ${PACLET_SOURCES} 321 | ${PROJECT_SOURCE_DIR}/CodeTools/Generate/CreatePacletArchive.wl 322 | ${PROJECT_SOURCE_DIR}/CodeTools/Generate/GenerateSources.wl 323 | ${PROJECT_SOURCE_DIR}/cmake/WolframScript.cmake 324 | VERBATIM 325 | WORKING_DIRECTORY 326 | ${PROJECT_BINARY_DIR} 327 | ) 328 | 329 | install( 330 | CODE 331 | "execute_process(COMMAND ${CMAKE_COMMAND} -DPACLET_OUTPUT=${PACLET_OUTPUT} -DPACLET_WOLFRAMVERSION=${PACLET_WOLFRAMVERSION} \"-DWOLFRAMKERNEL=${WOLFRAMKERNEL}\" -DKERNEL_TIMEOUT=${KERNEL_TIMEOUT} -P ${PROJECT_SOURCE_DIR}/cmake/InstallPaclet.cmake)" 332 | COMPONENT 333 | paclet 334 | ) 335 | -------------------------------------------------------------------------------- /CodeInspector/Documentation/English/Guides/CodeInspector.nb: -------------------------------------------------------------------------------- 1 | (* Content-type: application/vnd.wolfram.mathematica *) 2 | 3 | (*** Wolfram Notebook File ***) 4 | (* http://www.wolfram.com/nb *) 5 | 6 | (* CreatedBy='Mathematica 13.0' *) 7 | 8 | (*CacheID: 234*) 9 | (* Internal cache information: 10 | NotebookFileLineBreakTest 11 | NotebookFileLineBreakTest 12 | NotebookDataPosition[ 158, 7] 13 | NotebookDataLength[ 11281, 300] 14 | NotebookOptionsPosition[ 6865, 207] 15 | NotebookOutlinePosition[ 7332, 224] 16 | CellTagsIndexPosition[ 7289, 221] 17 | WindowFrame->Normal*) 18 | 19 | (* Beginning of Notebook Content *) 20 | Notebook[{ 21 | Cell[TextData[{ 22 | "New in: ", 23 | Cell["XX", "HistoryData", 24 | CellTags->"New",ExpressionUUID->"8e80b2a0-560d-4c9f-b6d9-e5cd82aaeb7d"], 25 | " | Modified in: ", 26 | Cell[" ", "HistoryData", 27 | CellTags->"Modified",ExpressionUUID->"7f576cac-a2bf-41c4-a31b-c10da059149a"], 28 | " | Obsolete in: ", 29 | Cell[" ", "HistoryData", 30 | CellTags->"Obsolete",ExpressionUUID->"bc2cb832-c7af-44c6-947c-b1846ce7801d"], 31 | " | Excised in: ", 32 | Cell[" ", "HistoryData", 33 | CellTags->"Excised",ExpressionUUID->"d1512049-139c-4ff6-95ee-ce46015fc07a"] 34 | }], "History", 35 | CellID->1247902091,ExpressionUUID->"c851296d-4c81-41eb-bf64-adf3bac5587a"], 36 | 37 | Cell[CellGroupData[{ 38 | 39 | Cell["Categorization", "CategorizationSection", 40 | CellID->1122911449,ExpressionUUID->"bfcb49dc-9a7d-4d95-9aa4-9778e91d49ab"], 41 | 42 | Cell["Guide", "Categorization", 43 | CellLabel->"Entity Type", 44 | CellID->686433507,ExpressionUUID->"a41f09e6-4c93-4d28-9074-a0a79f243852"], 45 | 46 | Cell["CodeInspector Package", "Categorization", 47 | CellLabel->"Paclet Name", 48 | CellID->605800465,ExpressionUUID->"128ebb07-9fca-4459-8bb5-397295824fc3"], 49 | 50 | Cell["CodeInspector`", "Categorization", 51 | CellLabel->"Context", 52 | CellID->468444828,ExpressionUUID->"1622b842-6f24-483a-8edb-67373da1c0ab"], 53 | 54 | Cell["CodeInspector/guide/CodeInspector", "Categorization", 55 | CellLabel->"URI",ExpressionUUID->"dacda237-8de5-431b-9293-e89d171025a3"] 56 | }, Closed]], 57 | 58 | Cell[CellGroupData[{ 59 | 60 | Cell["Keywords", "KeywordsSection", 61 | CellID->1427428552,ExpressionUUID->"1b5935ca-e5e4-43d6-ae97-eaf5d3282e5e"], 62 | 63 | Cell["XXXX", "Keywords", 64 | CellID->1251852827,ExpressionUUID->"34c0b773-84d1-4e40-8bcb-4fb8449edc21"] 65 | }, Closed]], 66 | 67 | Cell[CellGroupData[{ 68 | 69 | Cell["Details", "DetailsSection", 70 | CellID->307771771,ExpressionUUID->"ad550bf6-0af3-4816-bd89-d2751f74d638"], 71 | 72 | Cell["XXXX", "Details", 73 | CellLabel->"Lead", 74 | CellID->795394057,ExpressionUUID->"3ddda723-084a-4343-b92c-cb95447bc722"], 75 | 76 | Cell["XXXX", "Details", 77 | CellLabel->"Developers", 78 | CellID->350963985,ExpressionUUID->"46b84f5b-bb15-4ff7-bc39-7e032857f61f"], 79 | 80 | Cell["XXXX", "Details", 81 | CellLabel->"Authors", 82 | CellID->802101,ExpressionUUID->"b4e9145e-c074-4312-83a1-de7b06f96516"], 83 | 84 | Cell["XXXX", "Details", 85 | CellLabel->"Feature Name", 86 | CellID->509823116,ExpressionUUID->"dd59ec1a-a34a-45b6-8d50-dbd3b4a1b4de"], 87 | 88 | Cell["XXXX", "Details", 89 | CellLabel->"QA", 90 | CellID->199884516,ExpressionUUID->"b3246fab-81b1-489e-af37-285e5304972a"], 91 | 92 | Cell["XXXX", "Details", 93 | CellLabel->"DA", 94 | CellID->463951815,ExpressionUUID->"2c55a9fc-8ee4-45e5-be18-81f23cc4805d"], 95 | 96 | Cell["XXXX", "Details", 97 | CellLabel->"Docs", 98 | CellID->8237523,ExpressionUUID->"294147d2-dd7d-474e-8757-dea370537fd7"], 99 | 100 | Cell["XXXX", "Details", 101 | CellLabel->"Features Page Notes", 102 | CellID->813936640,ExpressionUUID->"20cf07f4-3c07-48c7-8b57-7267e8d2eef2"], 103 | 104 | Cell["XXXX", "Details", 105 | CellLabel->"Comments", 106 | CellID->240026365,ExpressionUUID->"e3e45107-5f75-45f1-94f3-8d7e98755c45"] 107 | }, Closed]], 108 | 109 | Cell[CellGroupData[{ 110 | 111 | Cell["CodeInspector", "GuideTitle", 112 | CellChangeTimes->{{3.8141764081011753`*^9, 3.814176408497032*^9}}, 113 | CellID->942062912,ExpressionUUID->"0001154e-ecc4-44fa-9d88-9520bee5535d"], 114 | 115 | Cell["\<\ 116 | CodeInspector is a package for finding and reporting problems in Wolfram \ 117 | Language code.\ 118 | \>", "GuideAbstract", 119 | CellChangeTimes->{ 120 | 3.791210026022861*^9, {3.791210141280633*^9, 3.791210141967534*^9}}, 121 | CellID->2001916300,ExpressionUUID->"3c8f4dd0-5d74-4105-8c15-48dbdb646c05"] 122 | }, Open ]], 123 | 124 | Cell[CellGroupData[{ 125 | 126 | Cell["", "GuideFunctionsSection", 127 | CellID->1866139230,ExpressionUUID->"0d3eba95-2ba2-41a1-a78b-f15494951860"], 128 | 129 | Cell[TextData[{ 130 | Cell[BoxData[ 131 | ButtonBox["CodeInspect", 132 | BaseStyle->"Link", 133 | ButtonData->"paclet:CodeInspector/ref/CodeInspect"]], 134 | "InlineGuideFunction",ExpressionUUID-> 135 | "b41f7024-3895-4e0e-a6dd-e227c69f3344"], 136 | " \[LongDash] returns a list of problems found in code." 137 | }], "GuideText", 138 | CellChangeTimes->{{3.791210349517922*^9, 3.7912103581824913`*^9}, { 139 | 3.791211513907338*^9, 3.7912115263838997`*^9}}, 140 | CellID->203374175,ExpressionUUID->"f3f401c7-db38-4516-ae76-1aac60440fdc"], 141 | 142 | Cell[TextData[{ 143 | Cell[BoxData[ 144 | ButtonBox["CodeInspectSummarize", 145 | BaseStyle->"Link", 146 | ButtonData->"paclet:CodeInspector/ref/CodeInspectSummarize"]], 147 | "InlineGuideFunction",ExpressionUUID-> 148 | "d3e794d8-a28f-4de2-86d0-12792e0bdb7f"], 149 | " \[LongDash] returns an inspection summary object." 150 | }], "GuideText", 151 | CellChangeTimes->{{3.791210367475873*^9, 3.79121037514272*^9}, { 152 | 3.7912105575369263`*^9, 3.791210557579248*^9}, 3.791211547151176*^9}, 153 | CellID->1463276848,ExpressionUUID->"1eddd189-3aba-4de6-acea-e71e5c5c8194"], 154 | 155 | Cell[TextData[{ 156 | Cell[BoxData[ 157 | ButtonBox["InspectionObject", 158 | BaseStyle->"Link", 159 | ButtonData->"paclet:CodeInspector/ref/InspectionObject"]], 160 | "InlineGuideFunction",ExpressionUUID-> 161 | "86128b1e-b070-48f4-9a6d-e19bb6ae15b6"], 162 | " \[LongDash] a problem found in WL source code." 163 | }], "GuideText", 164 | CellChangeTimes->{{3.791210367475873*^9, 3.79121037514272*^9}, { 165 | 3.791210547511422*^9, 3.791210568881468*^9}, 3.791211501413069*^9, 166 | 3.791211531567136*^9}, 167 | CellID->1444761988,ExpressionUUID->"2966f3d5-8f3b-4f1a-a215-846c1ddcbac6"], 168 | 169 | Cell[CellGroupData[{ 170 | 171 | Cell["\t", "GuideDelimiter", 172 | CellID->311258892,ExpressionUUID->"48803237-5222-4e07-bb71-4b51e4ad2c06"], 173 | 174 | Cell["XXXX . XXXX . ", "InlineGuideFunctionListing", 175 | CellID->58033752,ExpressionUUID->"a1ad4f4f-b64c-4da8-8005-df20fe886ab2"] 176 | }, Open ]] 177 | }, Open ]], 178 | 179 | Cell[CellGroupData[{ 180 | 181 | Cell["Tutorials", "GuideTutorialsSection", 182 | CellID->415694126,ExpressionUUID->"7b01a9e1-73ee-46da-a71f-1702ac7feec0"], 183 | 184 | Cell[TextData[ButtonBox["CodeInspector Tutorial", 185 | BaseStyle->"Link", 186 | ButtonData-> 187 | "paclet:CodeInspector/tutorial/CodeInspectorTutorial"]], "GuideTutorial", 188 | CellChangeTimes->{{3.791211437873395*^9, 3.7912114550595493`*^9}, 189 | 3.814176350853047*^9}, 190 | CellID->806871991,ExpressionUUID->"a7972e88-2430-4bc4-82ff-487b07ff2f6b"] 191 | }, Open ]], 192 | 193 | Cell[CellGroupData[{ 194 | 195 | Cell["More About", "GuideMoreAboutSection", 196 | CellID->23220180,ExpressionUUID->"579b68b0-a33d-439d-9870-fd2c5734bfef"], 197 | 198 | Cell["XXXX", "GuideMoreAbout", 199 | CellID->1567025153,ExpressionUUID->"83b9f469-5124-4e9b-90b2-3051399efb53"], 200 | 201 | Cell["XXXX", "GuideMoreAbout", 202 | CellID->252299663,ExpressionUUID->"c3bd4df2-4d8d-49f6-99c1-551d361fb06d"] 203 | }, Open ]], 204 | 205 | Cell["Related Links", "GuideRelatedLinksSection", 206 | CellID->415694148,ExpressionUUID->"732e1505-7081-401e-87ca-12244f9025a2"] 207 | }, 208 | WindowSize->{700, 770}, 209 | WindowMargins->{{4, Automatic}, {Automatic, 0}}, 210 | FrontEndVersion->"13.0 for Mac OS X x86 (64-bit) (October 17, 2021)", 211 | StyleDefinitions->FrontEnd`FileName[{"Wolfram"}, "GuidePageStyles.nb", 212 | CharacterEncoding -> "UTF-8"], 213 | ExpressionUUID->"f565a96f-5585-4af8-8a2b-0713ef93849f" 214 | ] 215 | (* End of Notebook Content *) 216 | 217 | (* Internal cache information *) 218 | (*CellTagsOutline 219 | CellTagsIndex->{} 220 | *) 221 | (*CellTagsIndex 222 | CellTagsIndex->{} 223 | *) 224 | (*NotebookFileOutline 225 | Notebook[{ 226 | Cell[558, 20, 599, 14, 24, "History",ExpressionUUID->"c851296d-4c81-41eb-bf64-adf3bac5587a", 227 | CellID->1247902091], 228 | Cell[CellGroupData[{ 229 | Cell[1182, 38, 123, 1, 29, "CategorizationSection",ExpressionUUID->"bfcb49dc-9a7d-4d95-9aa4-9778e91d49ab", 230 | CellID->1122911449], 231 | Cell[1308, 41, 133, 2, 70, "Categorization",ExpressionUUID->"a41f09e6-4c93-4d28-9074-a0a79f243852", 232 | CellID->686433507], 233 | Cell[1444, 45, 149, 2, 70, "Categorization",ExpressionUUID->"128ebb07-9fca-4459-8bb5-397295824fc3", 234 | CellID->605800465], 235 | Cell[1596, 49, 138, 2, 70, "Categorization",ExpressionUUID->"1622b842-6f24-483a-8edb-67373da1c0ab", 236 | CellID->468444828], 237 | Cell[1737, 53, 133, 1, 70, "Categorization",ExpressionUUID->"dacda237-8de5-431b-9293-e89d171025a3"] 238 | }, Closed]], 239 | Cell[CellGroupData[{ 240 | Cell[1907, 59, 111, 1, 19, "KeywordsSection",ExpressionUUID->"1b5935ca-e5e4-43d6-ae97-eaf5d3282e5e", 241 | CellID->1427428552], 242 | Cell[2021, 62, 100, 1, 70, "Keywords",ExpressionUUID->"34c0b773-84d1-4e40-8bcb-4fb8449edc21", 243 | CellID->1251852827] 244 | }, Closed]], 245 | Cell[CellGroupData[{ 246 | Cell[2158, 68, 108, 1, 19, "DetailsSection",ExpressionUUID->"ad550bf6-0af3-4816-bd89-d2751f74d638", 247 | CellID->307771771], 248 | Cell[2269, 71, 118, 2, 70, "Details",ExpressionUUID->"3ddda723-084a-4343-b92c-cb95447bc722", 249 | CellID->795394057], 250 | Cell[2390, 75, 124, 2, 70, "Details",ExpressionUUID->"46b84f5b-bb15-4ff7-bc39-7e032857f61f", 251 | CellID->350963985], 252 | Cell[2517, 79, 118, 2, 70, "Details",ExpressionUUID->"b4e9145e-c074-4312-83a1-de7b06f96516", 253 | CellID->802101], 254 | Cell[2638, 83, 126, 2, 70, "Details",ExpressionUUID->"dd59ec1a-a34a-45b6-8d50-dbd3b4a1b4de", 255 | CellID->509823116], 256 | Cell[2767, 87, 116, 2, 70, "Details",ExpressionUUID->"b3246fab-81b1-489e-af37-285e5304972a", 257 | CellID->199884516], 258 | Cell[2886, 91, 116, 2, 70, "Details",ExpressionUUID->"2c55a9fc-8ee4-45e5-be18-81f23cc4805d", 259 | CellID->463951815], 260 | Cell[3005, 95, 116, 2, 70, "Details",ExpressionUUID->"294147d2-dd7d-474e-8757-dea370537fd7", 261 | CellID->8237523], 262 | Cell[3124, 99, 133, 2, 70, "Details",ExpressionUUID->"20cf07f4-3c07-48c7-8b57-7267e8d2eef2", 263 | CellID->813936640], 264 | Cell[3260, 103, 122, 2, 70, "Details",ExpressionUUID->"e3e45107-5f75-45f1-94f3-8d7e98755c45", 265 | CellID->240026365] 266 | }, Closed]], 267 | Cell[CellGroupData[{ 268 | Cell[3419, 110, 178, 2, 77, "GuideTitle",ExpressionUUID->"0001154e-ecc4-44fa-9d88-9520bee5535d", 269 | CellID->942062912], 270 | Cell[3600, 114, 289, 6, 27, "GuideAbstract",ExpressionUUID->"3c8f4dd0-5d74-4105-8c15-48dbdb646c05", 271 | CellID->2001916300] 272 | }, Open ]], 273 | Cell[CellGroupData[{ 274 | Cell[3926, 125, 109, 1, 70, "GuideFunctionsSection",ExpressionUUID->"0d3eba95-2ba2-41a1-a78b-f15494951860", 275 | CellID->1866139230], 276 | Cell[4038, 128, 490, 11, 25, "GuideText",ExpressionUUID->"f3f401c7-db38-4516-ae76-1aac60440fdc", 277 | CellID->203374175], 278 | Cell[4531, 141, 525, 11, 25, "GuideText",ExpressionUUID->"1eddd189-3aba-4de6-acea-e71e5c5c8194", 279 | CellID->1463276848], 280 | Cell[5059, 154, 538, 12, 25, "GuideText",ExpressionUUID->"2966f3d5-8f3b-4f1a-a215-846c1ddcbac6", 281 | CellID->1444761988], 282 | Cell[CellGroupData[{ 283 | Cell[5622, 170, 103, 1, 26, "GuideDelimiter",ExpressionUUID->"48803237-5222-4e07-bb71-4b51e4ad2c06", 284 | CellID->311258892], 285 | Cell[5728, 173, 126, 1, 20, "InlineGuideFunctionListing",ExpressionUUID->"a1ad4f4f-b64c-4da8-8005-df20fe886ab2", 286 | CellID->58033752] 287 | }, Open ]] 288 | }, Open ]], 289 | Cell[CellGroupData[{ 290 | Cell[5903, 180, 117, 1, 72, "GuideTutorialsSection",ExpressionUUID->"7b01a9e1-73ee-46da-a71f-1702ac7feec0", 291 | CellID->415694126], 292 | Cell[6023, 183, 328, 6, 22, "GuideTutorial",ExpressionUUID->"a7972e88-2430-4bc4-82ff-487b07ff2f6b", 293 | CellID->806871991] 294 | }, Open ]], 295 | Cell[CellGroupData[{ 296 | Cell[6388, 194, 117, 1, 72, "GuideMoreAboutSection",ExpressionUUID->"579b68b0-a33d-439d-9870-fd2c5734bfef", 297 | CellID->23220180], 298 | Cell[6508, 197, 106, 1, 22, "GuideMoreAbout",ExpressionUUID->"83b9f469-5124-4e9b-90b2-3051399efb53", 299 | CellID->1567025153], 300 | Cell[6617, 200, 105, 1, 22, "GuideMoreAbout",ExpressionUUID->"c3bd4df2-4d8d-49f6-99c1-551d361fb06d", 301 | CellID->252299663] 302 | }, Open ]], 303 | Cell[6737, 204, 124, 1, 72, "GuideRelatedLinksSection",ExpressionUUID->"732e1505-7081-401e-87ca-12244f9025a2", 304 | CellID->415694148] 305 | } 306 | ] 307 | *) 308 | 309 | -------------------------------------------------------------------------------- /Tests/AggregateRules.mt: -------------------------------------------------------------------------------- 1 | 2 | Needs["CodeInspector`"] 3 | 4 | Needs["CodeParser`"] 5 | 6 | 7 | (* 8 | ImplicitTimesAcrossLines 9 | *) 10 | TestMatch[ 11 | CodeInspect["{ a\nb }"] 12 | , 13 | {InspectionObject["ImplicitTimesAcrossLines", _, _, _]} 14 | , 15 | TestID->"AggregateRules-20190522-D9Q1R2" 16 | ] 17 | 18 | (* 19 | ImplicitTimesBlanks 20 | *) 21 | TestMatch[ 22 | CodeInspect["{ ____ }"] 23 | , 24 | {InspectionObject["ImplicitTimesBlanks", _, _, _]} 25 | , 26 | TestID->"AggregateRules-20190522-M3F3T8" 27 | ] 28 | 29 | 30 | (* 31 | DotDifferentLine 32 | *) 33 | TestMatch[ 34 | CodeInspect["{ a.\nb }"] 35 | , 36 | {} 37 | , 38 | TestID->"AggregateRules-20190522-U4K0M9" 39 | ] 40 | 41 | TestMatch[ 42 | CodeInspect["{ a \n . b }"] 43 | , 44 | {InspectionObject["DifferentLine", _, _, _]} 45 | , 46 | TestID->"AggregateRules-20220622-Y3A4L1" 47 | ] 48 | 49 | 50 | (* 51 | SuspiciousSpan 52 | *) 53 | TestMatch[ 54 | CodeInspect[" a;; "] 55 | , 56 | { InspectionObject["SuspiciousSpan", _, _, _] } 57 | , 58 | TestID->"AggregateRules-20190522-S9S6W2" 59 | ] 60 | 61 | 62 | (* 63 | StraySemicolon 64 | *) 65 | TestMatch[ 66 | CodeInspect["f[ a;b; ;x ]"] 67 | , 68 | { InspectionObject["UnexpectedSemicolon", _, "Warning", _] } 69 | , 70 | TestID->"AggregateRules-20190630-H1H8N7" 71 | ] 72 | 73 | 74 | (* 75 | DifferentLine 76 | *) 77 | TestMatch[ 78 | CodeInspect["(f[] 79 | ; Throw[$Failed, $tag])"] 80 | , 81 | { InspectionObject["DifferentLine", _, "Remark", _] } 82 | , 83 | TestID->"AggregateRules-20190522-I8L1E6" 84 | ] 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | (* 93 | SuspiciousPatternTestCall 94 | *) 95 | TestMatch[ 96 | CodeInspect["a_?b[x]"] 97 | , 98 | {InspectionObject["SuspiciousPatternTestCall", _, _, _]} 99 | , 100 | TestID->"AggregateRules-20190522-X8Z0P3" 101 | ] 102 | 103 | 104 | 105 | 106 | 107 | (* 108 | AssociationCall 109 | *) 110 | TestMatch[ 111 | CodeInspect["a?Association"] 112 | , 113 | {InspectionObject["PatternTest", _, _, _], InspectionObject["AssociationCall", _, _, _]} 114 | , 115 | TestID->"AggregateRules-20190522-Y0P7H3" 116 | ] 117 | 118 | (* 119 | StringCall 120 | *) 121 | TestMatch[ 122 | CodeInspect["a?String"] 123 | , 124 | {InspectionObject["PatternTest", _, _, _], InspectionObject["StringCall", _, _, _]} 125 | , 126 | TestID->"AggregateRules-20190522-N1P9K9" 127 | ] 128 | 129 | (* 130 | IntegerCall 131 | *) 132 | TestMatch[ 133 | CodeInspect["a?Integer"] 134 | , 135 | {InspectionObject["PatternTest", _, _, _], InspectionObject["IntegerCall", _, _, _]} 136 | , 137 | TestID->"AggregateRules-20190522-I2E9A2" 138 | ] 139 | 140 | (* 141 | RealCall 142 | *) 143 | TestMatch[ 144 | CodeInspect["a?Real"] 145 | , 146 | {InspectionObject["PatternTest", _, _, _], InspectionObject["RealCall", _, _, _]} 147 | , 148 | TestID->"AggregateRules-20190522-S0H6K7" 149 | ] 150 | 151 | (* 152 | FailureCall 153 | *) 154 | TestMatch[ 155 | CodeInspect["a?Failure"] 156 | , 157 | {InspectionObject["PatternTest", _, _, _], InspectionObject["FailureCall", _, _, _]} 158 | , 159 | TestID->"AggregateRules-20190522-C8C7F4" 160 | ] 161 | 162 | 163 | 164 | 165 | (* 166 | SuspiciousRuleFunction 167 | *) 168 | 169 | TestMatch[ 170 | CodeInspect[" a->b& "] 171 | , 172 | {InspectionObject["SuspiciousRuleFunction", _, _, _]} 173 | , 174 | TestID->"AggregateRules-20190522-G2J4D6" 175 | ] 176 | 177 | TestMatch[ 178 | CodeInspect["(a->b&)"] 179 | , 180 | {} 181 | , 182 | TestID->"AggregateRules-20190522-N7H5I3" 183 | ] 184 | 185 | TestMatch[ 186 | CodeInspect["a->b& @ x"] 187 | , 188 | {} 189 | , 190 | TestID->"AggregateRules-20190522-Z0X7E6" 191 | ] 192 | 193 | 194 | TestMatch[ 195 | CodeInspect["Map[a->b&, x]"] 196 | , 197 | {} 198 | , 199 | TestID->"AggregateRules-20190522-D9J5I0" 200 | ] 201 | 202 | TestMatch[ 203 | CodeInspect["a->b&[x]"] 204 | , 205 | {} 206 | , 207 | TestID->"AggregateRules-20190522-U2S5F4" 208 | ] 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | (* 219 | SuspiciousPatternTestFunction 220 | *) 221 | TestMatch[ 222 | CodeInspect[" a?b& "] 223 | , 224 | {InspectionObject["PatternTest", _, _, _], InspectionObject["SuspiciousPatternTestFunction", _, _, _]} 225 | , 226 | TestID->"AggregateRules-20190523-U6J6X9" 227 | ] 228 | 229 | 230 | TestMatch[ 231 | CodeInspect["a_?b[x]"] 232 | , 233 | {InspectionObject["SuspiciousPatternTestCall", _, _, _]} 234 | , 235 | TestID->"AggregateRules-20210830-B4E6L0" 236 | 237 | ] 238 | 239 | 240 | (* 241 | SuspiciousPatternTestCallFunction 242 | *) 243 | TestMatch[ 244 | CodeInspect[" a?b[#]& "] 245 | , 246 | {InspectionObject["PatternTest", _, _, _], InspectionObject["SuspiciousPatternTestCallFunction", _, _, _]} 247 | , 248 | TestID->"AggregateRules-20190523-W2H4Z1" 249 | ] 250 | 251 | 252 | TestMatch[ 253 | CodeInspect["a_?b[x]&"] 254 | , 255 | {InspectionObject["SuspiciousPatternTestCallFunction", _, _, _]} 256 | , 257 | TestID->"AggregateRules-20210830-G1O8L9" 258 | ] 259 | 260 | 261 | TestMatch[ 262 | CodeInspect["a_?Association"] 263 | , 264 | {InspectionObject["AssociationCall", _, _, _]} 265 | , 266 | TestID->"AggregateRules-20210830-M3N7M8" 267 | ] 268 | 269 | 270 | TestMatch[ 271 | CodeInspect["a_?b:c"] 272 | , 273 | {InspectionObject["PatternTestPattern", _, _, _]} 274 | , 275 | TestID->"AggregateRules-20210830-G1S6J6" 276 | ] 277 | 278 | 279 | 280 | 281 | 282 | (* 283 | SuspiciousPatternBlankOptional 284 | *) 285 | TestMatch[ 286 | CodeInspect[" a_:_ "] 287 | , 288 | {InspectionObject["SuspiciousPatternBlankOptional", _, _, _]} 289 | , 290 | TestID->"AggregateRules-20190523-T7Q3D0" 291 | ] 292 | 293 | 294 | (* 295 | example taken from: 296 | https://bugs.wolfram.com/show?number=373945 297 | *) 298 | TestMatch[ 299 | CodeInspect["checkSystemID[sid_: ($platformPattern | Automatic)] := sid"] 300 | , 301 | {InspectionObject["SuspiciousPatternBlankOptional", _, _, _]} 302 | , 303 | TestID->"AggregateRules-20190523-B2O7A2" 304 | ] 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | (* 314 | SyntaxError: 315 | 316 | taken from SyntaxErrorNodes 317 | *) 318 | 319 | 320 | (* 321 | SyntaxError ExpectedOperand: 322 | *) 323 | 324 | TestMatch[ 325 | CodeInspect["{ + }"] 326 | , 327 | { 328 | InspectionObject["ExpectedOperand", _, "Fatal", _], 329 | InspectionObject["PrefixPlus", _, "Remark", _]} 330 | , 331 | TestID->"AggregateRules-20190523-C2Y2X2" 332 | ] 333 | 334 | TestMatch[ 335 | CodeInspect["{ * }"] 336 | , 337 | { 338 | InspectionObject["ExpectedOperand", _, "Fatal", _], 339 | InspectionObject["ExpectedOperand", _, "Fatal", _]} 340 | , 341 | TestID->"AggregateRules-20191212-O0N6L9" 342 | ] 343 | 344 | 345 | 346 | (* 347 | SyntaxError NonAssociative: 348 | *) 349 | 350 | TestMatch[ 351 | CodeInspect["a ? b ? c"] 352 | , 353 | {InspectionObject["NonAssociativePatternTest", _, _, _], InspectionObject["PatternTest", _, _, _], InspectionObject["PatternTest", _, _, _]} 354 | , 355 | TestID->"AggregateRules-20190523-W9O8V3" 356 | ] 357 | 358 | 359 | (* 360 | SyntaxError ExpectedTilde: 361 | *) 362 | 363 | TestMatch[ 364 | CodeInspect["a ~f"] 365 | , 366 | {InspectionObject["ExpectedTilde", _, _, _]} 367 | , 368 | TestID->"AggregateRules-20190523-N4B7X7" 369 | ] 370 | 371 | 372 | 373 | (* 374 | SyntaxError ExpectedSymbol: 375 | *) 376 | 377 | TestMatch[ 378 | CodeInspect["1:2"] 379 | , 380 | {InspectionObject["ExpectedSymbol", _, _, _]} 381 | , 382 | TestID->"AggregateRules-20190523-F9Z1T6" 383 | ] 384 | 385 | (* 386 | SyntaxError ExpectedSet: 387 | *) 388 | 389 | TestMatch[ 390 | CodeInspect["a /: b * c"] 391 | , 392 | {InspectionObject["ExpectedSet", _, _, _]} 393 | , 394 | TestID->"AggregateRules-20190523-A6O0J4" 395 | ] 396 | 397 | (* 398 | SyntaxError ExpectedPossible: 399 | *) 400 | 401 | TestMatch[ 402 | CodeInspect["&"] 403 | , 404 | {InspectionObject["ExpectedOperand", _, _, _]} 405 | , 406 | TestID->"AggregateRules-20190523-H9C7R8" 407 | ] 408 | 409 | 410 | 411 | 412 | TestMatch[ 413 | CodeInspect["8 Pi*\"M\""] 414 | , 415 | {InspectionObject["TimesString", _, _, _]} 416 | , 417 | TestID->"AggregateRules-20191121-L0N6E3" 418 | ] 419 | 420 | 421 | TestMatch[ 422 | CodeInspect["\ 423 | DocLinter`DocNotebookLint[dir_String?DirectoryQ, opts_:OptionsPattern[]] := Module[{nbs}, 424 | nbs = FileNames[\"*.nb\", dir, Infinity]; 425 | DocLinter`DocNotebookLint[nbs, opts] 426 | ]"] 427 | , 428 | {InspectionObject["SuspiciousPatternBlankOptional", _, _, _]} 429 | , 430 | TestID->"AggregateRules-20220302-Z0S4S7" 431 | ] 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | TestMatch[ 441 | CodeInspect["Function[{x, y}, x + y]&[1,2]"] 442 | , 443 | {InspectionObject["FunctionAmp", _, _, _]} 444 | , 445 | TestID->"AggregateRules-20220307-V3U4J1" 446 | ] 447 | 448 | 449 | 450 | TestMatch[ 451 | CodeInspect["\"a\" <> _"] 452 | , 453 | {InspectionObject["PatternStringJoin", _, "Error", _]} 454 | , 455 | TestID->"AggregateRules-20220310-Q4H6M5" 456 | ] 457 | 458 | 459 | 460 | Test[ 461 | CodeInspect["replaceheads_Alternatives : Alternatives[]"] 462 | , 463 | {InspectionObject["SuspiciousPatternBlankOptional", "Suspicious use of ``_Alternatives``.", "Warning", <| 464 | Source -> {{1, 1}, {1, 43}}, 465 | ConfidenceLevel -> 0.85, 466 | "AdditionalDescriptions" -> {"This may be ok if ``replaceheads`` is used as a pattern."}, 467 | CodeActions -> { 468 | CodeAction["Replace with ``replaceheads : Alternatives[]``", ReplaceNode, <| 469 | "ReplacementNode" -> 470 | BinaryNode[Pattern, { 471 | LeafNode[Symbol, "replaceheads", <|Source -> {{1, 1}, {1, 13}}|>], 472 | LeafNode[Token`Colon, ":", <||>], 473 | CallNode[LeafNode[Symbol, "Alternatives", <|Source -> {{1, 29}, {1, 41}}|>], 474 | GroupNode[GroupSquare, { 475 | LeafNode[Token`OpenSquare, "[", <|Source -> {{1, 41}, {1, 42}}|>], 476 | LeafNode[Token`CloseSquare, "]", <|Source -> {{1, 42}, {1, 43}}|>]}, <|Source -> {{1, 41}, {1, 43}}|>], <|Source -> {{1, 29}, {1, 43}}|>]}, <||>], Source -> {{1, 1}, {1, 43}}|>]}|>]} 477 | , 478 | TestID->"AggregateRules-20220311-K6D5T9" 479 | ] 480 | 481 | 482 | 483 | (* 484 | inspired by bug 421310 485 | *) 486 | 487 | TestMatch[ 488 | CodeInspect["audioData = Round[audioData**(2.^15-1)]"] 489 | , 490 | {InspectionObject["NonCommutativeMultiply", _, "Error", KeyValuePattern[ { 491 | CodeActions -> { 492 | CodeAction["Replace with ``*``", ReplaceNode, _], 493 | CodeAction["Replace with ``^``", ReplaceNode, _]} } ]]} 494 | , 495 | TestID->"AggregateRules-20220316-K3D9D9" 496 | ] 497 | 498 | 499 | 500 | 501 | (* 502 | inspired by bug 421311 503 | *) 504 | TestMatch[ 505 | CodeInspect["masking:_Image | All:_:All"] 506 | , 507 | {InspectionObject["NestedOptionals", _, "Error", _]} 508 | , 509 | TestID->"AggregateRules-20220316-I9J0H0" 510 | ] 511 | 512 | 513 | 514 | (* 515 | inspired by bug 421309 516 | *) 517 | TestMatch[ 518 | CodeInspect["r_?cQ:1"] 519 | , 520 | {InspectionObject["PatternTestPattern", _, "Error", KeyValuePattern[ { 521 | CodeActions -> { 522 | CodeAction["Insert ``:``", ReplaceNode, _], 523 | CodeAction["Replace with ``Optional``", ReplaceNode, _]} } ]]} 524 | , 525 | TestID->"AggregateRules-20220316-H3X3G7" 526 | ] 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | TestMatch[ 537 | CodeInspect["\ 538 | Replace[arg, { 539 | LHS := RHS 540 | }]"] 541 | , 542 | {InspectionObject["RuleSetConfusion", _, "Error", _]} 543 | , 544 | TestID->"AggregateRules-20220629-I8Q2E9" 545 | ] 546 | 547 | 548 | TestMatch[ 549 | CodeInspect["Replace[expr, lhs := rhs]"] 550 | , 551 | {InspectionObject["RuleSetConfusion", _, "Error", _]} 552 | , 553 | TestID->"AggregateRules-20220629-O6C4K5" 554 | ] 555 | 556 | TestMatch[ 557 | CodeInspect["ReplaceAll[expr, lhs := rhs]"] 558 | , 559 | {InspectionObject["RuleSetConfusion", _, "Error", _]} 560 | , 561 | TestID->"AggregateRules-20220629-J2Z3H0" 562 | ] 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | cst = CodeConcreteParse["f / a"] 571 | 572 | cst[[1]] = File 573 | 574 | TestMatch[ 575 | CodeInspectCST[cst] 576 | , 577 | {InspectionObject["TopLevelExpression", _, "Warning", KeyValuePattern[CodeActions -> { 578 | CodeAction["Replace ``/`` with ``/@``", ReplaceNode, _]}]]} 579 | , 580 | TestID->"AggregateRules-20220629-C3Z9W6" 581 | ] 582 | 583 | 584 | cst = CodeConcreteParse["f -> a"] 585 | 586 | cst[[1]] = File 587 | 588 | TestMatch[ 589 | CodeInspectCST[cst] 590 | , 591 | {InspectionObject["TopLevelExpression", _, "Warning", KeyValuePattern[CodeActions -> { 592 | CodeAction["Replace ``->`` with ``=``", ReplaceNode, _]}]]} 593 | , 594 | TestID->"AggregateRules-20220629-S7M1M8" 595 | ] 596 | 597 | 598 | cst = CodeConcreteParse["f :> a"] 599 | 600 | cst[[1]] = File 601 | 602 | TestMatch[ 603 | CodeInspectCST[cst] 604 | , 605 | {InspectionObject["TopLevelExpression", _, "Warning", KeyValuePattern[CodeActions -> { 606 | CodeAction["Replace ``:>`` with ``:=``", ReplaceNode, _]}]]} 607 | , 608 | TestID->"AggregateRules-20220629-U5R0R2" 609 | ] 610 | 611 | 612 | 613 | 614 | 615 | 616 | TestMatch[ 617 | CodeInspect["If[ a & b, then, else ]"] 618 | , 619 | {InspectionObject["ImplicitTimesFunction", _, "Error", _]} 620 | , 621 | TestID->"AggregateRules-20220629-Q1L0S1" 622 | ] 623 | 624 | 625 | 626 | TestMatch[ 627 | CodeInspect["a & b"] 628 | , 629 | {InspectionObject["ImplicitTimesFunction", _, "Error", _]} 630 | , 631 | TestID->"AggregateRules-20220629-S0J1G1" 632 | ] 633 | 634 | 635 | 636 | 637 | 638 | TestMatch[ 639 | CodeInspect["a:N_[]"] 640 | , 641 | {InspectionObject["SystemPattern", _, "Error", _]} 642 | , 643 | TestID->"AggregateRules-20220629-J3J0F3" 644 | ] 645 | 646 | 647 | TestMatch[ 648 | CodeInspect["a || b || c | d"] 649 | , 650 | {InspectionObject["AlternativesOr", _, "Error", _]} 651 | , 652 | TestID->"AggregateRules-20220630-A6H9G7" 653 | ] 654 | 655 | 656 | 657 | 658 | 659 | 660 | TestMatch[ 661 | CodeInspect["x = 1y == 2z == 3"] 662 | , 663 | {InspectionObject["SetInfixInequality", _, "Error", _]} 664 | , 665 | TestID->"AggregateRules-20221023-D6J1D0" 666 | ] 667 | 668 | 669 | 670 | 671 | TestMatch[ 672 | CodeInspect["\"a\" * \"b\""] 673 | , 674 | { 675 | InspectionObject["TimesString", _, _, _], 676 | InspectionObject["TimesString", _, _, _]} 677 | , 678 | TestID->"AggregateRules-20221023-V0H2W4" 679 | ] 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | -------------------------------------------------------------------------------- /CodeInspector/Generate/MakeLinterUIDockedCell.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | (* ::Section::Closed:: *) 4 | (*Package Header*) 5 | 6 | 7 | BeginPackage["LinterUIDockedCell`"] 8 | 9 | 10 | Begin["`Private`"] 11 | 12 | 13 | Needs["CodeInspector`"] 14 | 15 | 16 | (* ::Text:: *) 17 | (*The docked cell generated by this file needs to get written into CodeInspector.tr. Therefore, if you want to modify the docked cell by making a change in this make file, run all code in this notebook, and Lou's WIReS function at the end of this file will update the text resource. *) 18 | 19 | 20 | (* ::Section::Closed:: *) 21 | (*Lou's addDefinitions tools*) 22 | 23 | 24 | (* ::Text:: *) 25 | (*constructInit[syms] constructs an Initialization setting with the Definition of each of the given symbols. Think of it as an opt-in, non-recursive SaveDefinitions.*) 26 | 27 | 28 | SetAttributes[constructInit, HoldAllComplete] 29 | constructInit[syms__] := 30 | Module[{init}, 31 | init = HoldComplete /@ Unevaluated[{syms}]; 32 | init = Replace[init, HoldComplete[sym_] :> 33 | ToExpression[ToString[Definition[sym], InputForm], InputForm, HoldComplete], {1}]; 34 | init = Flatten[HoldComplete @@ init, Infinity, HoldComplete]; 35 | Replace[init, HoldComplete[exprs__] :> (Initialization :> {exprs})] 36 | ] 37 | 38 | 39 | SetAttributes[addDefinitions, HoldAllComplete] 40 | 41 | 42 | (* just in case you want to prepend to an existing Initialization *) 43 | addDefinitions[DynamicModule[args__, Initialization :> init_, opts___], {syms__}(*, firstAction:Hold[_]:Hold[Null]*)] := 44 | Replace[constructInit[syms], { 45 | (Initialization :> newinit_) :> DynamicModule[args, Initialization :> ((*First[firstAction]; *)newinit; init), opts], 46 | else_ :> DynamicModule[args, Initialization :> init, opts] 47 | }] 48 | 49 | 50 | (* it's easier if there's not another init setting already *) 51 | addDefinitions[DynamicModule[args__], {syms__Symbol}] := 52 | With[{newinit = constructInit[syms]}, DynamicModule[args, newinit]] 53 | 54 | 55 | (* ::Section::Closed:: *) 56 | (*Docked Cell*) 57 | 58 | 59 | $previewLength = 28; 60 | 61 | 62 | dockedCellMenuItem[notebook_NotebookObject, cell_CellObject] := 63 | With[{}, 64 | RuleDelayed[ 65 | (* Display a preview of the cell contents ($previewLength characters), and the severity count icons from the cell bracket button. *) 66 | Graphics[ 67 | { 68 | (* Display $previewStringLength characters of the cell contents on the left of the menu item. *) 69 | Inset[ 70 | With[ 71 | (* Use FrontEnd`ExportPacket to get a string of the cell contents. *) 72 | {expressionString = First[FrontEndExecute[ 73 | FrontEnd`ExportPacket[First[CodeInspector`LinterUI`Private`varValue[notebook, cell, "CellContents"]], "InputText"]]]}, 74 | (* Replace linebreaks with spaces. *) 75 | {noLineBreaks = StringReplace[expressionString, "\n" -> " "]}, 76 | (* Clip expressionString to the preview length. *) 77 | {previewString = StringTake[noLineBreaks, {1, UpTo[$previewLength]}]}, 78 | (* Add an elipsis to the end of the string if it was clipped, and make sure it fits within $previewLength. *) 79 | CodeInspector`LinterUI`Private`styleData["FixedWidth"][ 80 | If[StringLength[expressionString] > $previewLength, 81 | StringDrop[previewString, -1] <> "\[Ellipsis]", 82 | previewString]]], 83 | 84 | {-1, 0}, {-1, 0}], 85 | 86 | (* On the right of the menu item, display the same issue count icons as used in the cell bracket button. *) 87 | Inset[ 88 | CodeInspector`LinterUI`Private`lintSeverityCountsIconRow[cell], 89 | {1, 0}, {1, 0}] 90 | }, 91 | 92 | ImageSize -> {300, 25}, AspectRatio -> Full, PlotRange -> {{-1, 1}, {-1, 1}}, ImagePadding -> None], 93 | 94 | (* ...select the target cell and open any cell group that it might be in... *) 95 | SelectionMove[cell, All, Cell]; 96 | With[{nb = ParentNotebook[cell]}, FrontEndExecute[FrontEnd`FrontEndToken[nb, "OpenSelectionParents"]]]; 97 | (* ...and then scroll the notebook view to the cell *) 98 | SelectionMove[cell, After, Cell]]] 99 | 100 | 101 | dockedCellSeverityCountsButton[notebook_NotebookObject] := 102 | With[{formatIcon = Function[Show[#, ImageSize -> {13, 9}, BaselinePosition -> Scaled[-.2]]]}, 103 | ActionMenu[ 104 | Highlighted[ 105 | Row[{ 106 | CodeInspector`LinterUI`Private`lintSeverityCountsIconRow[notebook, "exclamSize" -> 12, FontSize -> 14, FontWeight -> Plain], 107 | Spacer[2], 108 | formatIcon[CodeInspector`LinterUI`Private`iconData["DownChevron"][CodeInspector`LinterUI`Private`colorData["UIDark"]]]}], 109 | 110 | ImageSize -> {Automatic, 19}, 111 | BaselinePosition -> Baseline, 112 | Background -> White, 113 | Frame -> True, 114 | FrameStyle -> Dynamic[If[CurrentValue["MouseOver"], Hue[0.55,0.82,0.87], GrayLevel[.8]]], 115 | FrameMargins -> {6{1, 1}, {1, 1}}, 116 | Alignment -> {Center, Baseline}], 117 | 118 | dockedCellMenuItem[notebook, #]& /@ 119 | CodeInspector`LinterUI`Private`varValue[notebook, All, "Cell"], 120 | 121 | Appearance -> None]] 122 | 123 | 124 | dockedCell = 125 | Cell[BoxData @ ToBoxes @ 126 | addDefinitions[ 127 | DynamicModule[{notebook}, 128 | Graphics[ 129 | { 130 | (* The left-aligned pod title and analysis-in-progress indicator. *) 131 | Inset[ 132 | Row[ 133 | { 134 | Pane[CodeInspector`LinterUI`Private`styleData["SectionHeader"]["Code Analysis"], BaselinePosition -> (Baseline -> Scaled[.65])], 135 | Spacer[8], 136 | Pane[ 137 | PaneSelector[ 138 | { 139 | (* If dockedCellPresentQ isn't True (meaning the docked cell is left over from a previous session) or kernelWasQuitQ is True, then display neither a loading indicator nor lint counts. *) 140 | {False, False} -> Spacer[0], 141 | {True, False} -> Spacer[0], 142 | (* Display an activity indicator while analysis is in progress. *) 143 | {True, True} -> Pane[ProgressIndicator[Appearance -> "Percolate"], BaselinePosition -> Scaled[.05]], 144 | (* Display the agregate severity counts for all cells in the notebook. *) 145 | {False, True} -> CodeInspector`LinterUI`Private`isolatedDynamic[ 146 | Dynamic[CodeInspector`LinterUI`Private`DynamicTriggers`dockedCellLintCounts], 147 | dockedCellSeverityCountsButton[notebook]]}, 148 | 149 | Dynamic[ 150 | (* Tracking FEPrivate`EvaluatorStatus["Local"] ensures that the PaneSelector updates if the kernel is quit. *) 151 | FEPrivate`EvaluatorStatus["Local"]; 152 | { 153 | CodeInspector`LinterUI`Private`varValue[notebook, "AnalysisInProgressQ"], 154 | TrueQ[CodeInspector`LinterUI`Private`varValue[notebook, "DockedCellPresentQ"]]}], 155 | 156 | ImageSize -> Automatic], 157 | 158 | BaselinePosition -> Scaled[.15]]}, 159 | Alignment -> Baseline], 160 | 161 | Offset[{8, 0}, {-1, 0}], {-1, 0}], 162 | 163 | (* Draw an "Analyze Notebook" or "Reanalyze Notebook" button depending on whether the CodeInspector` context is loaded. *) 164 | Inset[ 165 | PaneSelector[ 166 | { 167 | True -> CodeInspector`LinterUI`Private`button[ 168 | "Reanalyze Notebook", 169 | CodeInspector`LinterUI`Private`attachAnalysisAction[EvaluationNotebook[]], 170 | Method -> "Queued"], 171 | (* When the kernel is quit we must have no dependency on CodeInspector`LinterUI, hence the verbatim Button used here *) 172 | False -> Button[ 173 | 174 | Highlighted[ 175 | Style["Analyze Notebook", FontColor -> GrayLevel[0.2], FontFamily -> "Source Sans Pro", FontWeight -> Plain, FontSize -> 14], 176 | ImageSize -> {Automatic, 19}, 177 | FrameMargins -> {9{1, 1}, 0{1, 1}}, 178 | BaselinePosition -> Baseline, 179 | Alignment -> {Center, Center}, 180 | Background -> White, 181 | Frame -> True, 182 | FrameStyle -> Dynamic[If[CurrentValue["MouseOver"], Hue[0.55,0.82,0.87], GrayLevel[.8]]]], 183 | 184 | (* Delete docked cells with CellTags -> "AttachedAnalysisDockedCell" *) 185 | CurrentValue[EvaluationNotebook[], DockedCells] = 186 | With[{dockedCells = CurrentValue[EvaluationNotebook[], DockedCells]}, 187 | Pick[ 188 | dockedCells, 189 | Map[ 190 | Quiet[Options[#, CellTags]] =!= {CellTags -> "CodeAnalysisDockedCell"}&, 191 | dockedCells]]]; 192 | Needs["CodeInspector`"]; 193 | CodeInspector`AttachAnalysis[notebook], 194 | Appearance -> 195 | With[{suppressMouseDown9patch = 196 | Image[ 197 | NumericArray[{ 198 | {{255, 255, 255, 255}, {0, 0, 0, 255}, {255, 255, 255, 255}}, 199 | {{0, 0, 0, 255}, {0, 0, 0, 0}, {0, 0, 0, 255}}, 200 | {{255, 255, 255, 255}, {0, 0, 0, 255}, {255, 255, 255, 255}}}, "UnsignedInteger8"], 201 | "Byte", ColorSpace -> "RGB", ImageResolution -> {72, 72}, Interleaving -> True]}, 202 | {"Default" -> suppressMouseDown9patch, "Hover" -> suppressMouseDown9patch, "Pressed" -> suppressMouseDown9patch}], 203 | Method -> "Queued"]}, 204 | 205 | Dynamic[ 206 | (* Tracking FEPrivate`EvaluatorStatus["Local"] ensures that the PaneSelector updates if the kernel is quit. *) 207 | FEPrivate`EvaluatorStatus["Local"]; 208 | TrueQ[CodeInspector`LinterUI`Private`varValue[notebook, "DockedCellPresentQ"]]], 209 | 210 | ImageSize -> Automatic], 211 | 212 | Offset[{-26, 0}, {1, 0}], {1, 0}], 213 | 214 | (* Draw a "Close" button that clears the entire linter interface from a notebook. *) 215 | (* 216 | This Button does not need its Appearance suppressed because it is within Graphics. 217 | The typesetting step converts Button to EventHandler and EventHandler does not suffer from mouse-down OS appearance effects. *) 218 | Button[ 219 | Tooltip[ 220 | (* This is the resolved expression from evaluating CodeInspector`LinterUI`Private`closeIcon[{-11, 0}, {1, 0}] *) 221 | { 222 | GrayLevel[0.6], 223 | Disk[Offset[{-11, 0}, {1, 0}], Offset[6]], 224 | GrayLevel[0.97], AbsoluteThickness[1.5], CapForm["Round"], 225 | Line[{{Offset[{-13, 2}, {1, 0}], Offset[{-9, -2}, {1, 0}]}, {Offset[{-13, -2}, {1, 0}], Offset[{-9, 2}, {1, 0}]}}]}, 226 | "Close analysis", TooltipDelay -> 0], 227 | 228 | (* Delete the lint pods. *) 229 | NotebookDelete /@ Flatten[CodeInspector`LinterUI`Private`varValue[notebook, All, "UIAttachedCells"]]; 230 | 231 | (* Delete the clean cell bracket markers. *) 232 | NotebookDelete /@ Flatten[CodeInspector`LinterUI`Private`varValue[notebook, All, "CleanCellBracketMarker"]]; 233 | 234 | (* Delete docked cells with CellTags -> "AttachedAnalysisDockedCell" *) 235 | CurrentValue[EvaluationNotebook[], DockedCells] = 236 | With[{dockedCells = CurrentValue[EvaluationNotebook[], DockedCells]}, 237 | Pick[ 238 | dockedCells, 239 | Map[ 240 | Quiet[Options[#, CellTags]] =!= {CellTags -> "CodeAnalysisDockedCell"}&, 241 | dockedCells]]]; 242 | 243 | CodeInspector`LinterUI`Private`applyToVar[Remove, {EvaluationNotebook[], All}]; 244 | CodeInspector`LinterUI`Private`varSet[{notebook, "DockedCellPresentQ"}, False]] 245 | 246 | }, 247 | 248 | ImageSize -> {Full, 23}, AspectRatio -> Full, PlotRange -> {{-1, 1}, {-1, 1}}], 249 | 250 | Initialization :> ( 251 | notebook = EvaluationNotebook[]), 252 | 253 | Deinitialization :> CodeInspector`LinterUI`Private`varSet[{notebook, "DockedCellPresentQ"}, False], 254 | 255 | UnsavedVariables :> {notebook} 256 | ], 257 | 258 | (* Save the following definitions in the DynamicModule's Initialization option. *) 259 | {dockedCellSeverityCountsButton, dockedCellMenuItem, $previewLength, 260 | CodeInspector`LinterUI`Private`applyToVar, 261 | CodeInspector`LinterUI`Private`varValue, 262 | CodeInspector`LinterUI`Private`varSet, 263 | CodeInspector`LinterUI`Private`varNameString, 264 | CodeInspector`LinterUI`Private`extractFirstList}], 265 | 266 | Background -> GrayLevel[.97], 267 | (* Draw frame lines at the top and bottom of the cell. *) 268 | CellFrame -> {{0, 0}, {1, 1}}, CellFrameColor -> GrayLevel[.85], 269 | CellFrameMargins -> {{0, 0}, {0, 0}}, 270 | CellTags -> "CodeAnalysisDockedCell"]; 271 | 272 | 273 | (* ::Section::Closed:: *) 274 | (*Write the docked cell into CodeInspector.tr*) 275 | 276 | 277 | trPath = FileNameJoin[{ParentDirectory[NotebookDirectory[]], "FrontEnd", "TextResources", "CodeInspector.tr"}]; 278 | 279 | 280 | (* Ensure WIReS resources are visible *) 281 | ResourceFunction["AddResourceSystem", 282 | ResourceSystemBase -> "https://www.internalcloud.wolfram.com/obj/resourcesystem/api/1.0"]["WIReS"]; 283 | 284 | 285 | ResourceFunction["WriteTextResource"][trPath, "@@resource CodeInspectorExpressions", "DockedCell" -> dockedCell] 286 | 287 | 288 | (* ::Section::Closed:: *) 289 | (*Package Footer*) 290 | 291 | 292 | End[] 293 | EndPackage[] 294 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/UndocumentedSymbols.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"ActionDelay", "ActionMenuBox", "ActionMenuBoxOptions", "ActiveItem", 5 | "AlgebraicRulesData", "AlignmentMarker", "AllowAdultContent", 6 | "AllowChatServices", "AllowIncomplete", "Analytic", "AnimatorBox", 7 | "AnimatorBoxOptions", "AnimatorElements", "AppendCheck", "ArgumentCountQ", 8 | "Arrow3DBox", "ArrowBox", "Authenticate", "AutoEvaluateEvents", 9 | "AutoIndentSpacings", "AutoMatch", "AutomaticImageSize", 10 | "AutoNumberFormatting", "AutoQuoteCharacters", "AutoScaling", 11 | "AutoStyleOptions", "AutoStyleWords", "Axis3DBox", "Axis3DBoxOptions", 12 | "AxisBox", "AxisBoxOptions", "BackFaceColor", "BackFaceGlowColor", 13 | "BackFaceOpacity", "BackFaceSpecularColor", "BackFaceSpecularExponent", 14 | "BackFaceSurfaceAppearance", "BackFaceTexture", "BackgroundAppearance", 15 | "BackgroundTasksSettings", "Backsubstitution", "Beveled", 16 | "BezierCurve3DBox", "BezierCurve3DBoxOptions", "BezierCurveBox", 17 | "BezierCurveBoxOptions", "BlankForm", "Bounds", "Box", "BoxDimensions", 18 | "BoxForm", "BoxID", "BoxRotation", "BoxRotationPoint", "Bra", "BraKet", 19 | "BrowserCategory", "BSplineCurve3DBox", "BSplineCurve3DBoxOptions", 20 | "BSplineCurveBox", "BSplineCurveBoxOptions", "BSplineSurface3DBox", 21 | "BSplineSurface3DBoxOptions", "ButtonCell", "ButtonContents", 22 | "ButtonStyleMenuListing", "CachedValue", "CacheGraphics", 23 | "CardinalBSplineBasis", "CellBoundingBox", "CellContents", 24 | "CellElementsBoundingBox", "CellElementSpacings", "CellFrameStyle", 25 | "CellInsertionPointCell", "CellTrayPosition", "CellTrayWidgets", 26 | "ChangeOptions", "ChannelDatabin", "ChannelListenerWait", 27 | "ChannelPreSendFunction", "ChartElementData", "ChartElementDataFunction", 28 | "CheckAll", "CheckboxBox", "CheckboxBoxOptions", "CircleBox", 29 | "ClipboardNotebook", "ClockwiseContourIntegral", "Closed", "ClosingEvent", 30 | "CloudConnections", "CloudObjectInformation", "CloudObjectInformationData", 31 | "CloudUserID", "Coarse", "CoefficientDomain", "ColonForm", "ColorSetterBox", 32 | "ColorSetterBoxOptions", "ColumnBackgrounds", "CompilerEnvironmentAppend", 33 | "CompletionsListPacket", "ComponentwiseContextMenu", "CompressedData", 34 | "ConeBox", "ConicHullRegion3DBox", "ConicHullRegion3DBoxOptions", 35 | "ConicHullRegionBox", "ConicHullRegionBoxOptions", "Connect", 36 | "ContentsBoundingBox", "ContextMenu", "Continuation", "ContourIntegral", 37 | "ContourSmoothing", "ControlAlignment", "ControllerDuration", 38 | "ControllerInformationData", "ConvertToPostScript", 39 | "ConvertToPostScriptPacket", "Cookies", "CopyTag", "CounterBox", 40 | "CounterBoxOptions", "CounterClockwiseContourIntegral", "CounterEvaluator", 41 | "CounterStyle", "CuboidBox", "CuboidBoxOptions", "CurlyDoubleQuote", 42 | "CurlyQuote", "CylinderBox", "CylinderBoxOptions", "DampingFactor", 43 | "DataCompression", "DatasetDisplayPanel", "DateDelimiters", "DebugTag", 44 | "Decimal", "Default2DTool", "Default3DTool", "DefaultAttachedCellStyle", 45 | "DefaultControlPlacement", "DefaultDockedCellStyle", 46 | "DefaultInputFormatType", "DefaultOutputFormatType", "DefaultStyle", 47 | "DefaultTextFormatType", "DefaultTextInlineFormatType", "DefaultValue", 48 | "DefineExternal", "DegreeLexicographic", "DegreeReverseLexicographic", 49 | "DeleteWithContents", "DelimitedArray", "DestroyAfterEvaluation", 50 | "DeviceOpenQ", "DialogIndent", "DialogLevel", "DifferenceOrder", 51 | "DigitBlockMinimum", "DisableConsolePrintPacket", "DiskBox", 52 | "DiskBoxOptions", "DispatchQ", "DisplayRules", "DisplayTemporary", 53 | "DistributionDomain", "Divergence", "DocumentGeneratorInformationData", 54 | "DomainRegistrationInformation", "DOSTextFormat", "DoubleContourIntegral", 55 | "DoublyInfinite", "Down", "DrawBackFaces", "DrawFrontFaces", 56 | "DrawHighlighted", "DualLinearProgramming", "DumpGet", "DynamicBox", 57 | "DynamicBoxOptions", "DynamicLocation", "DynamicModuleBox", 58 | "DynamicModuleBoxOptions", "DynamicModuleParent", "DynamicName", 59 | "DynamicNamespace", "DynamicReference", "DynamicWrapperBox", 60 | "DynamicWrapperBoxOptions", "EditButtonSettings", "EliminationOrder", 61 | "EllipticReducedHalfPeriods", "EmbeddingObject", "EmphasizeSyntaxErrors", 62 | "Empty", "EnableConsolePrintPacket", "EndAdd", "EngineEnvironment", "Enter", 63 | "EqualColumns", "EqualRows", "EquatedTo", "ErrorBoxOptions", "ErrorNorm", 64 | "ErrorPacket", "ErrorsDialogSettings", "Evaluated", "EvaluationMode", 65 | "EvaluationOrder", "EvaluationRateLimit", "EventEvaluator", 66 | "EventHandlerTag", "ExactRootIsolation", "ExitDialog", "ExpectationE", 67 | "ExportPacket", "ExpressionPacket", "ExternalCall", "ExternalFunctionName", 68 | "Fail", "FEDisableConsolePrintPacket", "FEEnableConsolePrintPacket", 69 | "FileInformation", "FileName", "FilledCurveBox", "FilledCurveBoxOptions", 70 | "FillForm", "Fine", "FitAll", "FlashSelection", "Font", "FontName", 71 | "FontOpacity", "FontPostScriptName", "FontReencoding", "FormatRules", 72 | "FormatValues", "FrameInset", "Frameless", "FrontEndObject", 73 | "FrontEndResource", "FrontEndResourceString", "FrontEndStackSize", 74 | "FrontEndValueCache", "FrontEndVersion", "FrontFaceColor", 75 | "FrontFaceGlowColor", "FrontFaceOpacity", "FrontFaceSpecularColor", 76 | "FrontFaceSpecularExponent", "FrontFaceSurfaceAppearance", 77 | "FrontFaceTexture", "FullAxes", "GeneratedCellStyles", "Generic", 78 | "GeometricTransformation3DBox", "GeometricTransformation3DBoxOptions", 79 | "GeometricTransformationBox", "GeometricTransformationBoxOptions", 80 | "GestureHandlerTag", "GetContext", "GetFileName", 81 | "GetLinebreakInformationPacket", "GlobalPreferences", "GlobalSession", 82 | "Graphics3DBox", "Graphics3DBoxOptions", "GraphicsBaseline", "GraphicsBox", 83 | "GraphicsBoxOptions", "GraphicsComplex3DBox", "GraphicsComplex3DBoxOptions", 84 | "GraphicsComplexBox", "GraphicsComplexBoxOptions", "GraphicsContents", 85 | "GraphicsData", "GraphicsGridBox", "GraphicsGroup3DBox", 86 | "GraphicsGroup3DBoxOptions", "GraphicsGroupBox", "GraphicsGroupBoxOptions", 87 | "GraphicsGrouping", "GraphicsStyle", "GraphLayerLabels", "GraphRoot", 88 | "GreekStyle", "GridBoxAlignment", "GridBoxBackground", "GridBoxDividers", 89 | "GridBoxFrame", "GridBoxItemSize", "GridBoxItemStyle", "GridBoxOptions", 90 | "GridBoxSpacings", "GridElementStyleOptions", "GroupOpenerColor", 91 | "GroupOpenerInsideFrame", "GroupTogetherGrouping", 92 | "GroupTogetherNestedGrouping", "HeadCompose", "Headers", 93 | "HelpBrowserLookup", "HelpBrowserNotebook", "HelpViewerSettings", "Hessian", 94 | "HexahedronBox", "HexahedronBoxOptions", "HighlightString", "HomePage", 95 | "Horizontal", "HorizontalForm", "HorizontalScrollPosition", 96 | "HyperlinkCreationSettings", "HyphenationOptions", "IconizedObject", 97 | "IgnoreSpellCheck", "ImageCache", "ImageCacheValid", "ImageEditMode", 98 | "ImageMarkers", "ImageOffset", "ImageRangeCache", "ImageSizeCache", 99 | "ImageSizeRaw", "InactiveStyle", "IncludeSingularTerm", "Indent", 100 | "IndentingNewlineSpacings", "IndentMaxFraction", "IndexCreationOptions", 101 | "IndexTag", "Inequality", "InexactNumbers", "InformationData", 102 | "InformationDataGrid", "InlineCounterAssignments", 103 | "InlineCounterIncrements", "InlineRules", "InputFieldBox", 104 | "InputFieldBoxOptions", "InputGrouping", "InputSettings", 105 | "InputToBoxFormPacket", "InsertionPointObject", "Inset3DBox", 106 | "Inset3DBoxOptions", "InsetBox", "InsetBoxOptions", "Integral", 107 | "Interlaced", "InterpolationPrecision", "InterpretTemplate", 108 | "InterruptSettings", "Into", "InvisibleApplication", "InvisibleTimes", 109 | "ItemBox", "ItemBoxOptions", "Jacobian", "JoinedCurveBox", 110 | "JoinedCurveBoxOptions", "K", "KernelExecute", "Ket", "LabeledSlider", 111 | "LambertW", "LanguageOptions", "Launch", "LayoutInformation", 112 | "Lexicographic", "LicenseID", "Line3DBox", "Line3DBoxOptions", 113 | "LinearFilter", "LineBox", "LineBoxOptions", "LineBreak", 114 | "LinebreakSemicolonWeighting", "LineWrapParts", "LinkConnectedQ", 115 | "LinkError", "LinkFlush", "LinkHost", "LinkMode", "LinkOptions", 116 | "LinkReadHeld", "LinkService", "LinkWriteHeld", "Listen", 117 | "ListPickerBoxBackground", "LiteralSearch", "LocalizeDefinitions", 118 | "LocatorBox", "LocatorBoxOptions", "LocatorCentering", "LocatorPaneBox", 119 | "LocatorPaneBoxOptions", "LongEqual", "LongForm", "Loopback", "MachineID", 120 | "MachineName", "MacintoshSystemPageSetup", "MainSolve", 121 | "MaintainDynamicCaches", "MakeRules", "MatchLocalNameQ", "Material", 122 | "MathematicaNotation", "MathMLText", "MaxBend", "MaxPoints", "Menu", 123 | "MenuAppearance", "MenuEvaluator", "MenuItem", "MenuList", 124 | "MergeDifferences", "MessageObject", "MessageOptions", "MessagesNotebook", 125 | "MetaCharacters", "MethodOptions", "MinRecursion", "MinSize", "Mode", 126 | "Modular", "MonomialOrder", "MouseAppearanceTag", "MouseButtons", 127 | "MousePointerNote", "MultiLetterItalics", "MultiLetterStyle", 128 | "Multiplicity", "MultiscriptBoxOptions", "NamespaceBox", 129 | "NamespaceBoxOptions", "NBernoulliB", "NestedScriptRules", 130 | "NetworkPacketRecordingDuring", "Next", "NonAssociative", "NormalGrouping", 131 | "NotebookDefault", "NotebookInterfaceObject", "NProductFactors", 132 | "NSumTerms", "NValues", "ObjectExistsQ", "OLEData", "Open", "OpenerBox", 133 | "OpenerBoxOptions", "OpenFunctionInspectorPacket", "OpenSpecialOptions", 134 | "OptionQ", "OptionsPacket", "OptionValueBox", "OptionValueBoxOptions", 135 | "OutputFormData", "OutputGrouping", "OutputMathEditExpression", "Over", 136 | "OverlayBox", "OverlayBoxOptions", "Package", "PackPaclet", 137 | "PacletDirectoryAdd", "PacletDirectoryRemove", "PacletInformation", 138 | "PacletObjectQ", "PacletUpdate", "PageHeight", "PalettesMenuSettings", 139 | "PaneBox", "PaneBoxOptions", "PanelBox", "PanelBoxOptions", 140 | "PaneSelectorBox", "PaneSelectorBoxOptions", "PaperWidth", "Parameter", 141 | "ParameterVariables", "ParentConnect", "ParentForm", "Parenthesize", 142 | "ParentList", "PartialD", "PasteAutoQuoteCharacters", "PausedTime", 143 | "PeriodicInterpolation", "Perpendicular", "PickedElements", "PickMode", 144 | "Pivoting", "PlotRangeClipPlanesStyle", "Point3DBox", "Point3DBoxOptions", 145 | "PointBox", "PointBoxOptions", "Polygon3DBox", "Polygon3DBoxOptions", 146 | "PolygonBox", "PolygonBoxOptions", "PolygonHoleScale", "PolygonScale", 147 | "PolyhedronBox", "PolyhedronBoxOptions", "PolynomialForm", "Polynomials", 148 | "PopupMenuBox", "PopupMenuBoxOptions", "PostScript", "Precedence", 149 | "PredictionRoot", "PreferencesSettings", "Previous", "PrimaryPlaceholder", 150 | "PrintForm", "PrismBox", "PrismBoxOptions", "PrivateFrontEndOptions", 151 | "ProbabilityPr", "ProcessStateDomain", "ProcessTimeDomain", 152 | "ProgressIndicatorBox", "ProgressIndicatorBoxOptions", "PromptForm", 153 | "PyramidBox", "PyramidBoxOptions", "RadioButtonBox", 154 | "RadioButtonBoxOptions", "RandomSeed", "RangeSpecification", "Raster3DBox", 155 | "Raster3DBoxOptions", "RasterBox", "RasterBoxOptions", "RationalFunctions", 156 | "RawArray", "RawMedium", "RebuildPacletData", "RectangleBox", 157 | "RecurringDigitsForm", "ReferenceMarkers", "ReferenceMarkerStyle", 158 | "Reinstall", "Removed", "RepeatedString", "ResourceAcquire", 159 | "ResourceSubmissionObject", "ReturnCreatesNewCell", "ReturnEntersInput", 160 | "ReturnInputFormPacket", "RotationBox", "RotationBoxOptions", 161 | "RoundImplies", "RowBackgrounds", "RowHeights", "RuleCondition", "RuleForm", 162 | "SaveAutoDelete", "ScaledMousePosition", "ScheduledTaskInformationData", 163 | "ScriptForm", "ScriptRules", "SectionGrouping", "Selection", 164 | "SelectionCell", "SelectionCellCreateCell", "SelectionCellDefaultStyle", 165 | "SelectionCellParentStyle", "SelectionPlaceholder", "SelectWithContents", 166 | "SelfLoops", "ServiceResponse", "Setbacks", "SetOptionsPacket", 167 | "SetSecuredAuthenticationKey", "SetterBox", "SetterBoxOptions", 168 | "ShowAutoConvert", "ShowCodeAssist", "ShowControls", 169 | "ShowGroupOpenCloseIcon", "ShowInvisibleCharacters", 170 | "ShowPredictiveInterface", "ShowSyntaxStyles", "ShrinkWrapBoundingBox", 171 | "SingleEvaluation", "SingleLetterStyle", "Slider2DBox", 172 | "Slider2DBoxOptions", "Socket", "SolveDelayed", "SoundAndGraphics", "Space", 173 | "SpaceForm", "SpanningCharacters", "SphereBox", "SphereBoxOptions", 174 | "StartupSound", "StringBreak", "StringByteCount", "StripStyleOnPaste", 175 | "StrokeForm", "StructuredArrayHeadQ", "StyleKeyMapping", "StyleNames", 176 | "SurfaceAppearance", "Syntax", "SystemException", "SystemGet", 177 | "SystemInformationData", "SystemStub", "SystemTest", "Tab", "TableViewBox", 178 | "TableViewBoxAlignment", "TableViewBoxBackground", "TableViewBoxHeaders", 179 | "TableViewBoxItemSize", "TableViewBoxItemStyle", "TableViewBoxOptions", 180 | "TabViewBox", "TabViewBoxOptions", "TagBoxNote", "TagStyle", 181 | "TemplateEvaluate", "TemplateSlotSequence", "TemplateUnevaluated", 182 | "TemplateVerbatim", "TemporaryVariable", "TensorQ", "TetrahedronBox", 183 | "TetrahedronBoxOptions", "Text3DBox", "Text3DBoxOptions", "TextBand", 184 | "TextBoundingBox", "TextBox", "TextForm", "TextLine", "TextParagraph", 185 | "ThisLink", "TitleGrouping", "ToColor", "Toggle", "ToggleFalse", 186 | "TogglerBox", "TogglerBoxOptions", "TooBig", "TooltipBox", 187 | "TooltipBoxOptions", "TotalHeight", "TraceAction", "TraceInternal", 188 | "TraceLevel", "TrackCellChangeTimes", "TraditionalNotation", 189 | "TraditionalOrder", "TransparentColor", "TrapEnterKey", "TrapSelection", 190 | "TubeBezierCurveBox", "TubeBezierCurveBoxOptions", "TubeBox", 191 | "TubeBoxOptions", "TubeBSplineCurveBox", "TubeBSplineCurveBoxOptions", 192 | "UntrackedVariables", "Up", "UseGraphicsRange", "UserDefinedWavelet", 193 | "Using", "V2Get", "ValueBox", "ValueBoxOptions", "ValueForm", "ValuesData", 194 | "VectorGlyphData", "Verbose", "Vertical", "VerticalForm", 195 | "ViewPointSelectorSettings", "ViewPort", "VirtualGroupData", "VisibleCell", 196 | "WaitUntil", "WebPageMetaInformation", "WholeCellGroupOpener", 197 | "WindowPersistentStyles", "WindowSelected", "WindowWidth", 198 | "WolframAlphaDate", "WolframAlphaQuantity", "WolframAlphaResult", 199 | "WolframCloudSettings", "$ActivationGroupID", "$ActivationUserRegistered", 200 | "$AddOnsDirectory", "$BoxForms", "$CloudConnection", "$CloudVersionNumber", 201 | "$CloudWolframEngineVersionNumber", "$ConditionHold", "$DefaultMailbox", 202 | "$DefaultPath", "$FinancialDataSource", "$GeoEntityTypes", 203 | "$GeoLocationPrecision", "$HTMLExportRules", "$HTTPRequest", 204 | "$LaunchDirectory", "$LicenseProcesses", "$LicenseSubprocesses", 205 | "$LicenseType", "$LinkSupported", "$LoadedFiles", "$MaxLicenseProcesses", 206 | "$MaxLicenseSubprocesses", "$MinorReleaseNumber", "$NetworkLicense", "$Off", 207 | "$OutputForms", "$PatchLevelID", "$PermissionsGroupBase", "$PipeSupported", 208 | "$PreferencesDirectory", "$PrintForms", "$PrintLiteral", 209 | "$RegisteredDeviceClasses", "$RegisteredUserName", 210 | "$SecuredAuthenticationKeyTokens", "$SetParentLink", "$SoundDisplay", 211 | "$SuppressInputFormHeads", "$SystemMemory", "$TraceOff", "$TraceOn", 212 | "$TracePattern", "$TracePostAction", "$TracePreAction", 213 | "$UserAgentLanguages", "$UserAgentMachine", "$UserAgentName", 214 | "$UserAgentOperatingSystem", "$UserAgentVersion", "$UserName"} 215 | -------------------------------------------------------------------------------- /CodeInspector/Kernel/SuppressedRegions.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["CodeInspector`SuppressedRegions`"] 2 | 3 | SuppressedRegions 4 | 5 | suppressedRegion 6 | 7 | 8 | Begin["`Private`"] 9 | 10 | Needs["CodeParser`"] 11 | Needs["CodeParser`Utils`"] 12 | 13 | 14 | (* 15 | 16 | Example: 17 | 18 | (* :!CodeAnalysis::BeginBlock:: *) 19 | (* :!CodeAnalysis::Disable::DuplicateClauses::If:: *) 20 | 21 | If[a, b, b] 22 | 23 | (* :!CodeAnalysis::EndBlock:: *) 24 | 25 | 26 | *) 27 | 28 | 29 | codeInspectBeginPat = 30 | (* in-the-clear parsed from text *) 31 | LeafNode[Token`Comment, "(* :!CodeAnalysis::BeginBlock:: *)", _] | 32 | (* in-the-clear parsed from boxes *) 33 | CellNode[Cell, { 34 | GroupNode[Comment, { 35 | LeafNode[Token`Boxes`OpenParenStar, _, _], 36 | LeafNode[String, " ", _], 37 | BoxNode[RowBox, {{ 38 | LeafNode[String, ":", _], 39 | BoxNode[RowBox, {{ 40 | LeafNode[String, "!", _], 41 | BoxNode[RowBox, {{ 42 | LeafNode[String, "CodeAnalysis", _], 43 | LeafNode[String, "::", _], 44 | LeafNode[String, "BeginBlock", _], 45 | LeafNode[String, "::", _]}}, _]}}, _]}}, _], 46 | LeafNode[String, " ", _], 47 | LeafNode[Token`Boxes`StarCloseParen, _, _]}, _]}, _] 48 | 49 | codeInspectEndPat = 50 | (* in-the-clear parsed from text *) 51 | LeafNode[Token`Comment, "(* :!CodeAnalysis::EndBlock:: *)", _] | 52 | (* in-the-clear parsed from boxes *) 53 | CellNode[Cell, { 54 | GroupNode[Comment, { 55 | LeafNode[Token`Boxes`OpenParenStar, _, _], 56 | LeafNode[String, " ", _], 57 | BoxNode[RowBox, {{ 58 | LeafNode[String, ":", _], 59 | BoxNode[RowBox, {{ 60 | LeafNode[String, "!", _], 61 | BoxNode[RowBox, {{ 62 | LeafNode[String, "CodeAnalysis", _], 63 | LeafNode[String, "::", _], 64 | LeafNode[String, "EndBlock", _], 65 | LeafNode[String, "::", _]}}, _]}}, _]}}, _], 66 | LeafNode[String, " ", _], 67 | LeafNode[Token`Boxes`StarCloseParen, _, _]}, _]}, _] 68 | 69 | codeInspectPackagePat = 70 | (* in-the-blind parsed from text *) 71 | LeafNode[Token`Comment, str_String /; StringMatchQ[str, "(* ::Package::\"Tags\"" ~~ ___ ~~ ":: *)"], _] | 72 | (* in-the-blind parsed from boxes *) 73 | CellNode[Cell, { 74 | GroupNode[Comment, { 75 | LeafNode[Token`Boxes`OpenParenStar, _, _], 76 | LeafNode[String, " ", _], 77 | BoxNode[RowBox, {{ 78 | BoxNode[RowBox, {{ 79 | LeafNode[String, "::", _], 80 | LeafNode[String, "Package", _], 81 | LeafNode[String, "::", _], 82 | LeafNode[String, "\"Tags\"", _]}}, _], 83 | LeafNode[String, "->", _], _}}, _], 84 | LeafNode[String, " ", _], 85 | LeafNode[Token`Boxes`StarCloseParen, _, _]}, _]}, _] 86 | 87 | codeInspectCellPat = 88 | (* in-the-blind parsed from text *) 89 | LeafNode[Token`Comment, str_String /; StringMatchQ[str, "(* ::Code::Initialization::\"Tags\"" ~~ ___ ~~ ":: *)"], _] | 90 | (* in-the-blind parsed from boxes *) 91 | CellNode[Cell, { 92 | GroupNode[Comment, { 93 | LeafNode[Token`Boxes`OpenParenStar, _, _], 94 | LeafNode[String, " ", _], 95 | BoxNode[RowBox, {{ 96 | BoxNode[RowBox, {{ 97 | LeafNode[String, "::", _], 98 | LeafNode[String, "Code", _], 99 | LeafNode[String, "::", _], 100 | LeafNode[String, "Initialization", _], 101 | LeafNode[String, "::", _], 102 | LeafNode[String, "\"Tags\"", _]}}, _], 103 | LeafNode[String, "->", _], _}}, _], 104 | LeafNode[String, " ", _], 105 | LeafNode[Token`Boxes`StarCloseParen, _, _]}, _]}, _] 106 | 107 | codeInspectSuppressPat = 108 | (* in-the-clear with no arg parsed from text *) 109 | LeafNode[ 110 | Token`Comment, 111 | str_String /; 112 | StringMatchQ[str, 113 | ("(* :!CodeAnalysis::Disable::" ~~ (LetterCharacter ~~ LetterCharacter...) ~~ ":: *)") 114 | ], 115 | _ 116 | ] | 117 | (* in-the-clear with 1 arg parsed from text *) 118 | LeafNode[ 119 | Token`Comment, 120 | str_String /; 121 | StringMatchQ[str, 122 | ("(* :!CodeAnalysis::Disable::" ~~ (LetterCharacter ~~ LetterCharacter...) ~~ "::" ~~ (LetterCharacter ~~ LetterCharacter...) ~~ ":: *)") 123 | ], 124 | _ 125 | ] | 126 | (* in-the-clear with no arg parsed from boxes *) 127 | CellNode[Cell, { 128 | GroupNode[Comment, { 129 | LeafNode[Token`Boxes`OpenParenStar, _, _], 130 | LeafNode[String, " ", _], 131 | BoxNode[RowBox, {{ 132 | LeafNode[String, ":", _], 133 | BoxNode[RowBox, {{ 134 | LeafNode[String, "!", _], 135 | BoxNode[RowBox, {{ 136 | LeafNode[String, "CodeAnalysis", _], 137 | LeafNode[String, "::", _], 138 | LeafNode[String, "Disable", _], 139 | LeafNode[String, "::", _], 140 | LeafNode[String, _, _], 141 | LeafNode[String, "::", _]}}, _]}}, _]}}, _], 142 | LeafNode[String, " ", _], 143 | LeafNode[Token`Boxes`StarCloseParen, _, _]}, _]}, _] | 144 | (* in-the-clear with 1 arg parsed from boxes *) 145 | CellNode[Cell, { 146 | GroupNode[Comment, { 147 | LeafNode[Token`Boxes`OpenParenStar, _, _], 148 | LeafNode[String, " ", _], 149 | BoxNode[RowBox, {{ 150 | LeafNode[String, ":", _], 151 | BoxNode[RowBox, {{ 152 | LeafNode[String, "!", _], 153 | BoxNode[RowBox, {{ 154 | LeafNode[String, "CodeAnalysis", _], 155 | LeafNode[String, "::", _], 156 | LeafNode[String, "Disable", _], 157 | LeafNode[String, "::", _], 158 | LeafNode[String, _, _], 159 | LeafNode[String, "::", _], 160 | LeafNode[String, _, _], 161 | LeafNode[String, "::", _]}}, _]}}, _]}}, _], 162 | LeafNode[String, " ", _], 163 | LeafNode[Token`Boxes`StarCloseParen, _, _]}, _]}, _] 164 | 165 | 166 | 167 | 168 | 169 | SuppressedRegions[cstIn_] := 170 | Catch[ 171 | Module[{cst, codeInspectBeginPatNodePoss, suppressedRegions, siblingsPos, siblings, endFound, candidate, endPos, suppresseds, beginPos}, 172 | 173 | cst = cstIn; 174 | 175 | suppressedRegions = {}; 176 | 177 | If[empty[cst[[2]]], 178 | Throw[suppressedRegions] 179 | ]; 180 | 181 | (* 182 | ::Package:: affecting the entire file 183 | 184 | (* ::Package::"Tags"-><|"DuplicateClauses" -> <|"If" -> <|Enabled -> False|>|>|>:: *) 185 | 186 | If[a,b,b] 187 | 188 | *) 189 | candidate = cst[[2, 1]]; 190 | If[MatchQ[candidate, codeInspectPackagePat], 191 | beginPos = {2, 1}; 192 | endPos = {2, Length[cst[[2]]]}; 193 | suppresseds = suppressedsFromCandidate[candidate]; 194 | AppendTo[suppressedRegions, 195 | suppressedRegion[rangeStart[Extract[cst, beginPos][[3]]], rangeEnd[Extract[cst, endPos][[3]]], suppresseds, <| "Toplevel" -> True |>] 196 | ] 197 | ]; 198 | 199 | (* 200 | ::Code::Initialization:: affecting the next child 201 | *) 202 | Do[ 203 | candidate = cst[[2, i]]; 204 | If[MatchQ[candidate, codeInspectCellPat] && MatchQ[cst[[2, i + 1]], LeafNode[Token`Newline, _, _]], 205 | suppresseds = suppressedsFromCandidate[candidate]; 206 | AppendTo[suppressedRegions, 207 | suppressedRegion[rangeStart[Extract[cst, {2, i + 2}][[3]]], rangeEnd[Extract[cst, {2, i + 2}][[3]]], suppresseds, <| "Toplevel" -> True |>] 208 | ] 209 | ] 210 | , 211 | {i, 1, Length[cst[[2]]] - 2} 212 | ]; 213 | 214 | codeInspectBeginPatNodePoss = Position[cst, codeInspectBeginPat]; 215 | 216 | If[$Debug, 217 | Print["codeInspectBeginPatNodePoss: ", codeInspectBeginPatNodePoss] 218 | ]; 219 | 220 | Do[ 221 | siblingsPos = Most[beginPos]; 222 | siblings = Extract[cst, {siblingsPos}][[1]]; 223 | endFound = False; 224 | suppresseds = {}; 225 | Do[ 226 | candidate = siblings[[pos]]; 227 | Switch[candidate, 228 | codeInspectEndPat, 229 | endPos = Most[beginPos] ~Join~ {pos}; 230 | endFound = True; 231 | Break[] 232 | , 233 | codeInspectSuppressPat, 234 | suppresseds = suppresseds ~Join~ suppressedsFromCandidate[candidate] 235 | ] 236 | , 237 | {pos, Last[beginPos]+1, Length[siblings]} 238 | ]; 239 | If[endFound, 240 | AppendTo[suppressedRegions, 241 | suppressedRegion[rangeStart[Extract[cst, beginPos][[3]]], rangeEnd[Extract[cst, endPos][[3]]], suppresseds, <| "Toplevel" -> MatchQ[beginPos, {_, _}] |>] 242 | ] 243 | , 244 | Message[SuppressedRegions::missingpop]; 245 | Throw[{}] 246 | ] 247 | , 248 | {beginPos, codeInspectBeginPatNodePoss} 249 | ]; 250 | 251 | suppressedRegions 252 | ]] 253 | 254 | 255 | (* 256 | in-the-clear with no arg parsed from text 257 | *) 258 | suppressedsFromCandidate[ 259 | LeafNode[ 260 | Token`Comment, 261 | str_String /; 262 | StringMatchQ[str, 263 | ("(* :!CodeAnalysis::Disable::" ~~ (LetterCharacter ~~ LetterCharacter...) ~~ ":: *)") 264 | ], 265 | _ 266 | ] 267 | ] := 268 | StringCases[str, { 269 | "(* :!CodeAnalysis::Disable::" ~~ d:(LetterCharacter ~~ LetterCharacter...) ~~ ":: *)" :> {d} 270 | }] 271 | 272 | (* 273 | in-the-clear with 1 arg parsed from text 274 | *) 275 | suppressedsFromCandidate[ 276 | LeafNode[ 277 | Token`Comment, 278 | str_String /; 279 | StringMatchQ[str, 280 | ("(* :!CodeAnalysis::Disable::" ~~ (LetterCharacter ~~ LetterCharacter...) ~~ "::" ~~ (LetterCharacter ~~ LetterCharacter...) ~~ ":: *)") 281 | ], 282 | _ 283 | ] 284 | ] := 285 | StringCases[str, { 286 | "(* :!CodeAnalysis::Disable::" ~~ d:(LetterCharacter ~~ LetterCharacter...) ~~ "::" ~~ a:(LetterCharacter ~~ LetterCharacter...) ~~ ":: *)" :> {d, a} 287 | }] 288 | 289 | (* 290 | in-the-clear with no arg parsed from boxes 291 | *) 292 | suppressedsFromCandidate[ 293 | CellNode[Cell, { 294 | GroupNode[Comment, { 295 | LeafNode[Token`Boxes`OpenParenStar, _, _], 296 | LeafNode[String, " ", _], 297 | BoxNode[RowBox, {{ 298 | LeafNode[String, ":", _], 299 | BoxNode[RowBox, {{ 300 | LeafNode[String, "!", _], 301 | BoxNode[RowBox, {{ 302 | LeafNode[String, "CodeAnalysis", _], 303 | LeafNode[String, "::", _], 304 | LeafNode[String, "Disable", _], 305 | LeafNode[String, "::", _], 306 | LeafNode[String, d_, _], 307 | LeafNode[String, "::", _]}}, _]}}, _]}}, _], 308 | LeafNode[String, " ", _], 309 | LeafNode[Token`Boxes`StarCloseParen, _, _]}, _]}, _] 310 | ] := {{d}} 311 | 312 | (* 313 | in-the-clear with 1 arg parsed from boxes 314 | *) 315 | suppressedsFromCandidate[ 316 | CellNode[Cell, { 317 | GroupNode[Comment, { 318 | LeafNode[Token`Boxes`OpenParenStar, _, _], 319 | LeafNode[String, " ", _], 320 | BoxNode[RowBox, {{ 321 | LeafNode[String, ":", _], 322 | BoxNode[RowBox, {{ 323 | LeafNode[String, "!", _], 324 | BoxNode[RowBox, {{ 325 | LeafNode[String, "CodeAnalysis", _], 326 | LeafNode[String, "::", _], 327 | LeafNode[String, "Disable", _], 328 | LeafNode[String, "::", _], 329 | LeafNode[String, d_, _], 330 | LeafNode[String, "::", _], 331 | LeafNode[String, a_, _], 332 | LeafNode[String, "::", _]}}, _]}}, _]}}, _], 333 | LeafNode[String, " ", _], 334 | LeafNode[Token`Boxes`StarCloseParen, _, _]}, _]}, _] 335 | ] := {{d, a}} 336 | 337 | (* in-the-blind parsed from text *) 338 | suppressedsFromCandidate[ 339 | LeafNode[Token`Comment, str_String /; StringMatchQ[str, "(* ::Package::\"Tags\"->" ~~ ___ ~~ ":: *)"], _] 340 | ] := 341 | Module[{assocStr}, 342 | assocStr = StringCases[str, "(* ::Package::\"Tags\"->" ~~ a___ ~~ ":: *)" :> a][[1]]; 343 | suppressedsFromCandidateAssocStr[assocStr] 344 | ] 345 | 346 | (* in-the-blind parsed from text *) 347 | suppressedsFromCandidate[ 348 | LeafNode[Token`Comment, str_String /; StringMatchQ[str, "(* ::Code::Initialization::\"Tags\"->" ~~ ___ ~~ ":: *)"], _] 349 | ] := 350 | Module[{assocStr}, 351 | assocStr = StringCases[str, "(* ::Code::Initialization::\"Tags\"->" ~~ a___ ~~ ":: *)" :> a][[1]]; 352 | suppressedsFromCandidateAssocStr[assocStr] 353 | ] 354 | 355 | 356 | (* in-the-blind parsed from text *) 357 | suppressedsFromCandidateAssocStr[assocStr_String] := 358 | Catch[ 359 | Reap[ 360 | Module[{assocNode, rules1, tag, body1, rules2, body2, rules3, argument}, 361 | assocNode = CodeParse[assocStr]; 362 | If[!MatchQ[assocNode, ContainerNode[String, {CallNode[LeafNode[Symbol, "Association", _], _, _]}, _]], 363 | (* 364 | invalid 365 | *) 366 | Throw[{}] 367 | ]; 368 | rules1 = assocNode[[2, 1, 2]]; 369 | Do[ 370 | If[!MatchQ[rule1, CallNode[LeafNode[Symbol, "Rule", _], {LeafNode[String, _, _], _}, _]], 371 | (* 372 | invalid 373 | *) 374 | Throw[{}] 375 | ]; 376 | tag = FromNode[rule1[[2, 1]]]; 377 | body1 = rule1[[2, 2]]; 378 | If[!MatchQ[body1, CallNode[LeafNode[Symbol, "Association", _], _, _]], 379 | (* 380 | invalid 381 | *) 382 | Throw[{}] 383 | ]; 384 | rules2 = body1[[2]]; 385 | Do[ 386 | Switch[rule2, 387 | CallNode[LeafNode[Symbol, "Rule", _], {LeafNode[Symbol, "Enabled", _], LeafNode[Symbol, "False", _]}, _], 388 | Sow[{tag}] 389 | , 390 | CallNode[LeafNode[Symbol, "Rule", _], {LeafNode[String, _, _], _}, _], 391 | argument = FromNode[rule2[[2, 1]]]; 392 | body2 = rule2[[2, 2]]; 393 | If[!MatchQ[body2, CallNode[LeafNode[Symbol, "Association", _], _, _]], 394 | (* 395 | invalid 396 | *) 397 | Throw[{}] 398 | ]; 399 | rules3 = body2[[2]]; 400 | Do[ 401 | Switch[rule3, 402 | CallNode[LeafNode[Symbol, "Rule", _], {LeafNode[Symbol, "Enabled", _], LeafNode[Symbol, "False", _]}, _], 403 | Sow[{tag, argument}] 404 | , 405 | _, 406 | (* 407 | invalid 408 | *) 409 | Throw[{}] 410 | ] 411 | , 412 | {rule3, rules3} 413 | ] 414 | , 415 | _, 416 | (* 417 | invalid 418 | *) 419 | Throw[{}] 420 | ] 421 | , 422 | {rule2, rules2} 423 | ] 424 | , 425 | {rule1, rules1} 426 | ] 427 | ] (* Module *) 428 | ][[2, 1]] (* Reap *) 429 | ] (* Catch *) 430 | 431 | 432 | 433 | 434 | (* 435 | Check for CellIndex before checking for Source 436 | *) 437 | rangeStart[KeyValuePattern[CellIndex -> index_]] := 438 | <| CellIndex -> index |> 439 | 440 | rangeEnd[KeyValuePattern[CellIndex -> index_]] := 441 | <| CellIndex -> index |> 442 | 443 | 444 | (* 445 | For LineColumn convention, return the start or end span 446 | *) 447 | rangeStart[KeyValuePattern[Source -> {s:{_Integer, _Integer}, {_Integer, _Integer}}]] := 448 | <| Source -> s |> 449 | 450 | rangeEnd[KeyValuePattern[Source -> {{_Integer, _Integer}, e:{_Integer, _Integer}}]] := 451 | <| Source -> e |> 452 | 453 | 454 | (* 455 | For SourceCharacterIndex convention, return the start or end span 456 | *) 457 | rangeStart[KeyValuePattern[Source -> {s:_Integer, _Integer}]] := 458 | <| Source -> s |> 459 | 460 | rangeEnd[KeyValuePattern[Source -> {_Integer, e:_Integer}]] := 461 | <| Source -> e |> 462 | 463 | 464 | (* 465 | For other conventions, just return the src 466 | 467 | We do not know what to do 468 | *) 469 | rangeStart[KeyValuePattern[Source -> src_]] := 470 | <| Source -> src |> 471 | 472 | rangeEnd[KeyValuePattern[Source -> src_]] := 473 | <| Source -> src |> 474 | 475 | 476 | End[] 477 | 478 | EndPackage[] 479 | -------------------------------------------------------------------------------- /Tests/AbstractRules.mt: -------------------------------------------------------------------------------- 1 | 2 | Needs["CodeInspector`"] 3 | 4 | Needs["CodeParser`"] 5 | 6 | 7 | (* 8 | StringCall 9 | *) 10 | TestMatch[ 11 | CodeInspect["{ String[1,2,3] }"] 12 | , 13 | {InspectionObject["BadCall", _, _, _]} 14 | , 15 | TestID->"AbstractRules-20190522-C4I4L9" 16 | ] 17 | 18 | 19 | (* 20 | IntegerCall 21 | *) 22 | TestMatch[ 23 | CodeInspect["{ Integer[1,2,3] }"] 24 | , 25 | {InspectionObject["BadCall", _, _, _]} 26 | , 27 | TestID->"AbstractRules-20190523-F3D4K1" 28 | ] 29 | 30 | (* 31 | RealCall 32 | *) 33 | TestMatch[ 34 | CodeInspect["{ Real[1,2,3] }"] 35 | , 36 | {InspectionObject["BadCall", _, _, _]} 37 | , 38 | TestID->"AbstractRules-20190523-N1Z9G4" 39 | ] 40 | 41 | 42 | 43 | (* 44 | DuplicateKeys 45 | *) 46 | TestMatch[ 47 | CodeInspect["<| 1->2, 1->3 |>"] 48 | , 49 | {InspectionObject["DuplicateKeys", _, _, _]} 50 | , 51 | TestID->"AbstractRules-20190523-M5A4H9" 52 | ] 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | (* 61 | WhichArguments 62 | *) 63 | TestMatch[ 64 | CodeInspect[" Which[] "] 65 | , 66 | {InspectionObject["Arguments", _, _, _]} 67 | , 68 | TestID->"AbstractRules-20190523-J1K9V9" 69 | ] 70 | 71 | TestMatch[ 72 | CodeInspect[" Which[1] "] 73 | , 74 | {InspectionObject["Arguments", _, _, _]} 75 | , 76 | TestID->"AbstractRules-20190523-V5Y3C5" 77 | ] 78 | 79 | 80 | 81 | (* 82 | SwitchWhichConfusion 83 | *) 84 | TestMatch[ 85 | CodeInspect[" Which[$OperatingSystem, 1] "] 86 | , 87 | {InspectionObject["SwitchWhichConfusion", _, _, _]} 88 | , 89 | TestID->"AbstractRules-20190523-K1F7I6" 90 | ] 91 | 92 | TestMatch[ 93 | CodeInspect[" Which[a, b, _, 1] "] 94 | , 95 | {InspectionObject["SwitchWhichConfusion", _, _, _]} 96 | , 97 | TestID->"AbstractRules-20190523-E9G8B8" 98 | ] 99 | 100 | 101 | (* 102 | DuplicateClauses 103 | *) 104 | 105 | TestMatch[ 106 | CodeInspect[" Which[a, 1, a, 2, b, 3, b, 4] "] 107 | , 108 | {InspectionObject["DuplicateClauses", _, _, _], InspectionObject["DuplicateClauses", _, _, _]} 109 | , 110 | TestID->"AbstractRules-20190523-X9U0J9" 111 | ] 112 | 113 | 114 | 115 | 116 | 117 | 118 | (* 119 | SwitchArguments 120 | *) 121 | TestMatch[ 122 | CodeInspect[" Switch[] "] 123 | , 124 | {InspectionObject["Arguments", _, _, _]} 125 | , 126 | TestID->"AbstractRules-20190523-D4B0S0" 127 | ] 128 | 129 | TestMatch[ 130 | CodeInspect[" Switch[1] "] 131 | , 132 | {InspectionObject["Arguments", _, _, _]} 133 | , 134 | TestID->"AbstractRules-20190523-E2M2S7" 135 | ] 136 | 137 | TestMatch[ 138 | CodeInspect[" Switch[1, 2] "] 139 | , 140 | {InspectionObject["Arguments", _, _, _]} 141 | , 142 | TestID->"AbstractRules-20190523-O2W9G5" 143 | ] 144 | 145 | (* 146 | OperatingSystemLinux 147 | *) 148 | TestMatch[ 149 | CodeInspect[" Switch[$OperatingSystem, \"Linux\", 2] "] 150 | , 151 | {InspectionObject["OperatingSystemLinux", _, _, _]} 152 | , 153 | TestID->"AbstractRules-20190523-Q2B2S9" 154 | ] 155 | 156 | 157 | (* 158 | SwitchWhichConfusion 159 | *) 160 | TestMatch[ 161 | CodeInspect[" Switch[a, b, 1, True, 2] "] 162 | , 163 | {InspectionObject["SwitchWhichConfusion", _, _, _]} 164 | , 165 | TestID->"AbstractRules-20190523-B0P8P4" 166 | ] 167 | 168 | 169 | (* 170 | DuplicateClauses 171 | *) 172 | TestMatch[ 173 | CodeInspect[" Switch[a, 1, 2, 1, 2] "] 174 | , 175 | {InspectionObject["DuplicateClauses", _, _, _]} 176 | , 177 | TestID->"AbstractRules-20190523-X0L3I6" 178 | ] 179 | 180 | 181 | 182 | 183 | 184 | (* 185 | DuplicateClauses 186 | *) 187 | TestMatch[ 188 | CodeInspect[" If[a, b, b] "] 189 | , 190 | {InspectionObject["DuplicateClauses", _, _, _]} 191 | , 192 | TestID->"AbstractRules-20190717-B6J9M6" 193 | ] 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | (* 203 | DuplicateNamedPattern 204 | *) 205 | 206 | TestMatch[ 207 | CodeInspect[" a : ( a:2 ) "] 208 | , 209 | {InspectionObject["DuplicatePatternName", _, _, _]} 210 | , 211 | TestID->"AbstractRules-20190523-I6L1Y3" 212 | ] 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | (* 221 | Control 222 | *) 223 | 224 | TestMatch[ 225 | CodeInspect[" f[Return] "] 226 | , 227 | {InspectionObject["Control", _, _, _]} 228 | , 229 | TestID->"AbstractRules-20190523-D3W5H4" 230 | ] 231 | 232 | TestMatch[ 233 | CodeInspect[" f[Break] "] 234 | , 235 | {InspectionObject["Control", _, _, _]} 236 | , 237 | TestID->"AbstractRules-20190523-C0B7T6" 238 | ] 239 | 240 | TestMatch[ 241 | CodeInspect[" f[Continue] "] 242 | , 243 | {InspectionObject["Control", _, _, _]} 244 | , 245 | TestID->"AbstractRules-20190523-T4U6R6" 246 | ] 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | (* 259 | ModuleArguments 260 | *) 261 | TestMatch[ 262 | CodeInspect[" Module[] "] 263 | , 264 | {InspectionObject["Arguments", _, _, _]} 265 | , 266 | TestID->"AbstractRules-20190523-B5L5C3" 267 | ] 268 | 269 | TestMatch[ 270 | CodeInspect[" Module[1] "] 271 | , 272 | {InspectionObject["Arguments", _, _, _]} 273 | , 274 | TestID->"AbstractRules-20190523-C7Y5J5" 275 | ] 276 | 277 | TestMatch[ 278 | CodeInspect[" Module[1, 2] "] 279 | , 280 | {InspectionObject["Arguments", _, _, _]} 281 | , 282 | TestID->"AbstractRules-20190523-S4E7T5" 283 | ] 284 | 285 | TestMatch[ 286 | CodeInspect[" Module[{}] "] 287 | , 288 | {InspectionObject["Arguments", _, _, _]} 289 | , 290 | TestID->"AbstractRules-20190523-H1R4I0" 291 | ] 292 | 293 | (* 294 | DuplicateVariables 295 | *) 296 | TestMatch[ 297 | CodeInspect[" Module[{a, a}, a+1] "] 298 | , 299 | {InspectionObject["DuplicateVariables", _, _, _]} 300 | , 301 | TestID->"AbstractRules-20190523-O2P3W5" 302 | ] 303 | 304 | (* 305 | UnusedVariables 306 | *) 307 | TestMatch[ 308 | CodeInspect[" Module[{a}, b] "] 309 | , 310 | {InspectionObject["UnusedVariable", _, _, _]} 311 | , 312 | TestID->"AbstractRules-20190523-A3S7S9" 313 | ] 314 | 315 | TestMatch[ 316 | CodeInspect[" Module[{a}, b] "] 317 | , 318 | {InspectionObject["UnusedVariable", _, _, _]} 319 | , 320 | TestID->"AbstractRules-20190523-V4X7X0" 321 | ] 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | (* 332 | DynamicModuleArguments 333 | *) 334 | TestMatch[ 335 | CodeInspect[" DynamicModule[] "] 336 | , 337 | {InspectionObject["Arguments", _, _, _]} 338 | , 339 | TestID->"AbstractRules-20190523-L8J1T4" 340 | ] 341 | 342 | TestMatch[ 343 | CodeInspect[" DynamicModule[1] "] 344 | , 345 | {InspectionObject["Arguments", _, _, _]} 346 | , 347 | TestID->"AbstractRules-20190523-W2N3A3" 348 | ] 349 | 350 | TestMatch[ 351 | CodeInspect[" DynamicModule[1, 2] "] 352 | , 353 | {InspectionObject["Arguments", _, _, _]} 354 | , 355 | TestID->"AbstractRules-20190523-V4L5J8" 356 | ] 357 | 358 | TestMatch[ 359 | CodeInspect[" DynamicModule[{}] "] 360 | , 361 | {InspectionObject["Arguments", _, _, _]} 362 | , 363 | TestID->"AbstractRules-20190523-D0L1F5" 364 | ] 365 | 366 | (* 367 | DuplicateVariables 368 | *) 369 | TestMatch[ 370 | CodeInspect[" DynamicModule[{a, a}, a+1] "] 371 | , 372 | {InspectionObject["DuplicateVariables", _, _, _]} 373 | , 374 | TestID->"AbstractRules-20190523-X3I6F8" 375 | ] 376 | 377 | (* 378 | UnusedVariables 379 | *) 380 | 381 | TestMatch[ 382 | CodeInspect[" DynamicModule[{a}, b] "] 383 | , 384 | {InspectionObject["UnusedVariable", _, _, _]} 385 | , 386 | TestID->"AbstractRules-20190523-I7M0H0" 387 | ] 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | (* 396 | WithArguments 397 | *) 398 | TestMatch[ 399 | CodeInspect[" With[] "] 400 | , 401 | {InspectionObject["Arguments", _, _, _]} 402 | , 403 | TestID->"AbstractRules-20190523-S0M2V2" 404 | ] 405 | 406 | TestMatch[ 407 | CodeInspect[" With[1] "] 408 | , 409 | {InspectionObject["Arguments", _, _, _]} 410 | , 411 | TestID->"AbstractRules-20190523-U9S3P9" 412 | ] 413 | 414 | TestMatch[ 415 | CodeInspect[" With[1, 2] "] 416 | , 417 | {InspectionObject["Arguments", _, _, _]} 418 | , 419 | TestID->"AbstractRules-20190523-M8E9Y5" 420 | ] 421 | 422 | TestMatch[ 423 | CodeInspect[" With[{}] "] 424 | , 425 | {InspectionObject["Arguments", _, _, _]} 426 | , 427 | TestID->"AbstractRules-20190523-X9G8L3" 428 | ] 429 | 430 | TestMatch[ 431 | CodeInspect["With[{}, {}, 34]"] 432 | , 433 | {InspectionObject["NoParameters", _, _, _], InspectionObject["NoParameters", _, _, _]} 434 | , 435 | TestID -> "AbstractRules-20191120-N6B9B5" 436 | ] 437 | 438 | (* 439 | DuplicateVariables 440 | *) 441 | TestMatch[ 442 | CodeInspect[" With[{a=1, a=2}, a+1] "] 443 | , 444 | {InspectionObject["DuplicateParameters", _, _, _]} 445 | , 446 | TestID->"AbstractRules-20190523-I2P7L0" 447 | ] 448 | 449 | (* 450 | UnusedVariables 451 | *) 452 | 453 | TestMatch[ 454 | CodeInspect[" With[{a=2}, b] "] 455 | , 456 | {InspectionObject["UnusedParameter", _, _, _]} 457 | , 458 | TestID->"AbstractRules-20190523-Q7Z2V1" 459 | ] 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | (* 471 | BlockArguments 472 | *) 473 | TestMatch[ 474 | CodeInspect[" Block[] "] 475 | , 476 | {InspectionObject["Arguments", _, _, _]} 477 | , 478 | TestID->"AbstractRules-20190523-O5T9E8" 479 | ] 480 | 481 | TestMatch[ 482 | CodeInspect[" Block[1] "] 483 | , 484 | {InspectionObject["Arguments", _, _, _]} 485 | , 486 | TestID->"AbstractRules-20190523-P0N3N7" 487 | ] 488 | 489 | TestMatch[ 490 | CodeInspect[" Block[1, 2] "] 491 | , 492 | {InspectionObject["Arguments", _, _, _]} 493 | , 494 | TestID->"AbstractRules-20190523-M5D6K6" 495 | ] 496 | 497 | TestMatch[ 498 | CodeInspect[" Block[{}] "] 499 | , 500 | {InspectionObject["Arguments", _, _, _]} 501 | , 502 | TestID->"AbstractRules-20190523-Y7G9I9" 503 | ] 504 | 505 | 506 | 507 | (* 508 | DuplicateVariables 509 | *) 510 | TestMatch[ 511 | CodeInspect[" Block[{a, a}, a+1] "] 512 | , 513 | {InspectionObject["DuplicateVariables", _, _, _]} 514 | , 515 | TestID->"AbstractRules-20190523-Y6N0J1" 516 | ] 517 | 518 | (* 519 | UnusedBlockVariables 520 | *) 521 | 522 | TestMatch[ 523 | CodeInspect[" Block[{a}, b] "] 524 | , 525 | {InspectionObject["UnusedVariable", _, _, _]} 526 | , 527 | TestID->"AbstractRules-20190523-Q7K3O8" 528 | ] 529 | 530 | 531 | 532 | 533 | (* 534 | NamedPatternInOptional 535 | *) 536 | 537 | TestMatch[ 538 | CodeInspect[" _:b:c "] 539 | , 540 | {InspectionObject["NamedPatternInOptional", _, _, _]} 541 | , 542 | TestID->"AbstractRules-20190523-V0R4Z2" 543 | ] 544 | 545 | 546 | 547 | 548 | 549 | 550 | (* 551 | BadSymbol 552 | *) 553 | 554 | TestMatch[ 555 | CodeInspect[" Failed "] 556 | , 557 | {InspectionObject["BadSymbol", _, _, _]} 558 | , 559 | TestID->"AbstractRules-20190523-I3X1Y6" 560 | ] 561 | 562 | 563 | TestMatch[ 564 | CodeInspect[" AnyFalse "] 565 | , 566 | {InspectionObject["BadSymbol", _, _, _]} 567 | , 568 | TestID->"AbstractRules-20190523-U6Q9N5" 569 | ] 570 | 571 | TestMatch[ 572 | CodeInspect[" AllFalse "] 573 | , 574 | {InspectionObject["BadSymbol", _, _, _]} 575 | , 576 | TestID->"AbstractRules-20190523-X6A5I6" 577 | ] 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | (* 591 | SelfAssignment 592 | *) 593 | TestMatch[ 594 | CodeInspect[" a = a "] 595 | , 596 | {InspectionObject["SelfAssignment", _, _, _]} 597 | , 598 | TestID->"AbstractRules-20190523-G9E3U1" 599 | ] 600 | 601 | 602 | 603 | 604 | 605 | (* 606 | LoadJavaClassSystem 607 | *) 608 | 609 | TestMatch[ 610 | CodeInspect[" LoadJavaClass[\"java.lang.System\"] "] 611 | , 612 | {InspectionObject["LoadJavaClassSystem", _, _, _]} 613 | , 614 | TestID->"AbstractRules-20190523-L1S8Q4" 615 | ] 616 | 617 | TestMatch[ 618 | CodeInspect[" JLink`LoadJavaClass[\"java.lang.System\"] "] 619 | , 620 | {InspectionObject["LoadJavaClassSystem", _, _, _]} 621 | , 622 | TestID->"AbstractRules-20190523-L5T8B9" 623 | ] 624 | 625 | 626 | 627 | 628 | 629 | 630 | (* 631 | SuspiciousPrivateContext 632 | *) 633 | 634 | sample = FileNameJoin[{DirectoryName[$CurrentTestSource], "files", "sample.wl"}] 635 | 636 | 637 | 638 | TestMatch[ 639 | CodeInspect[File[sample]] 640 | , 641 | {InspectionObject["SuspiciousPrivateContext", _, _, _]} 642 | , 643 | TestID->"AbstractRules-20190523-U0K0P7" 644 | ] 645 | 646 | 647 | 648 | 649 | 650 | 651 | Test[ 652 | CodeInspect["a ~~ b c"] 653 | , 654 | {InspectionObject[ 655 | "ImplicitTimesInStringExpression", 656 | "Suspicious implicit ``Times`` in ``StringExpression``.", 657 | "Error", 658 | <|Source -> {{1, 6}, {1, 9}}, ConfidenceLevel -> 0.95|>]} 659 | , 660 | TestID->"AbstractRules-20220211-P3T9Q1" 661 | ] 662 | 663 | 664 | Test[ 665 | CodeInspect["MatchQ[a, \"b\" ~~ c]"] 666 | , 667 | {InspectionObject[ 668 | "StringPatternInMatchQ", 669 | "String pattern inside of ``MatchQ``.", 670 | "Error", <| 671 | Source -> {{1, 1}, {1, 7}}, 672 | ConfidenceLevel -> 0.8, 673 | CodeActions -> { 674 | CodeAction[ 675 | "Replace with ``StringMatchQ``", 676 | ReplaceNode, 677 | <|Source -> {{1, 1}, {1, 7}}, 678 | "ReplacementNode" -> LeafNode[Symbol, "StringMatchQ", <||>]|>]}|>]} 679 | , 680 | TestID->"AbstractRules-20220211-N3Y5N7" 681 | ] 682 | 683 | Test[ 684 | CodeInspect["MatchQ[a, RegularExpression[\"b\"]]"] 685 | , 686 | {InspectionObject[ 687 | "StringPatternInMatchQ", 688 | "String pattern inside of ``MatchQ``.", 689 | "Error", <| 690 | Source -> {{1, 1}, {1, 7}}, 691 | ConfidenceLevel -> 0.8, 692 | CodeActions -> { 693 | CodeAction[ 694 | "Replace with ``StringMatchQ``", 695 | ReplaceNode, 696 | <|Source -> {{1, 1}, {1, 7}}, 697 | "ReplacementNode" -> LeafNode[Symbol, "StringMatchQ", <||>]|>]}|>]} 698 | , 699 | TestID->"AbstractRules-20220211-W4S8Q8" 700 | ] 701 | 702 | Test[ 703 | CodeInspect["MatchQ[a, LetterCharacter]"] 704 | , 705 | {InspectionObject[ 706 | "StringPatternInMatchQ", 707 | "String pattern inside of ``MatchQ``.", 708 | "Error", <| 709 | Source -> {{1, 1}, {1, 7}}, 710 | ConfidenceLevel -> 0.8, 711 | CodeActions -> { 712 | CodeAction[ 713 | "Replace with ``StringMatchQ``", 714 | ReplaceNode, 715 | <|Source -> {{1, 1}, {1, 7}}, 716 | "ReplacementNode" -> LeafNode[Symbol, "StringMatchQ", <||>]|>]}|>]} 717 | , 718 | TestID->"AbstractRules-20220211-E4D6M4" 719 | ] 720 | 721 | 722 | 723 | (* 724 | from bug 419646 725 | *) 726 | TestMatch[ 727 | CodeInspect["MatchQ[f[{g}, <|a -> b|>], f[{___, g, ___}, KeyValuePattern[_ -> _]]]"] 728 | , 729 | {InspectionObject["KernelBug", _, "Error", _]} 730 | , 731 | TestID->"AbstractRules-20220323-C4Q6P2" 732 | ] 733 | 734 | (* 735 | try to not get false positives 736 | *) 737 | TestMatch[ 738 | CodeInspect["KeyValuePattern[\"Definitions\" -> {___, LeafNode[Symbol, tokenSymbol, _Association], ___}]"] 739 | , 740 | {} 741 | , 742 | TestID->"AbstractRules-20220323-I9E9I8" 743 | ] 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | (* 755 | bug 425484 756 | *) 757 | 758 | TestMatch[ 759 | CodeInspect["Function[{C}, C+1]"] 760 | , 761 | {InspectionObject["SystemParameter", _, "Error", _]} 762 | , 763 | TestID->"AbstractRules-20220629-C4X0Q0" 764 | ] 765 | 766 | TestMatch[ 767 | CodeInspect["Function[Null, #+1]"] 768 | , 769 | {} 770 | , 771 | TestID->"AbstractRules-20220629-D5O4G0" 772 | ] 773 | 774 | 775 | TestMatch[ 776 | CodeInspect["Module[{C}, C+1]"] 777 | , 778 | {InspectionObject["SystemVariable", _, "Error", _]} 779 | , 780 | TestID->"AbstractRules-20220629-P3F1V0" 781 | ] 782 | 783 | 784 | TestMatch[ 785 | CodeInspect["With[{C = 1}, C+1]"] 786 | , 787 | {InspectionObject["SystemParameter", _, "Error", _]} 788 | , 789 | TestID->"AbstractRules-20220629-Q9M1H7" 790 | ] 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | TestMatch[ 799 | CodeInspect["Select[a, b == c]"] 800 | , 801 | {InspectionObject["PseudoPredicate", _, "Warning", _]} 802 | , 803 | TestID->"AbstractRules-20220629-X1H4P3" 804 | ] 805 | 806 | 807 | 808 | 809 | 810 | 811 | lints = CodeInspect["Break"] 812 | 813 | actions = lints[[1, 4, Key[CodeActions]]] 814 | 815 | node = actions[[1, 3, "ReplacementNode"]] 816 | 817 | Test[ 818 | ToSourceCharacterString[node] 819 | , 820 | "Break[]" 821 | , 822 | TestID->"AbstractRules-20220915-O6D9M7" 823 | ] 824 | 825 | 826 | lints = CodeInspect["LoadJavaClass[\"java.lang.System\"]"] 827 | 828 | actions = lints[[1, 4, Key[CodeActions]]] 829 | 830 | node = actions[[1, 3, "ReplacementNode"]] 831 | 832 | Test[ 833 | ToSourceCharacterString[node] 834 | , 835 | "LoadJavaClass[\"java.lang.System\", AllowShortContext->False]" 836 | , 837 | TestID->"AbstractRules-20220915-S6J0M3" 838 | ] 839 | 840 | 841 | 842 | 843 | -------------------------------------------------------------------------------- /CodeInspector/Generate/MakeCodeAnalysisOptionsPalette.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | (* ::Section::Closed:: *) 4 | (*Package Header*) 5 | 6 | If[!MemberQ[$Path, #], PrependTo[$Path, #]]&[DirectoryName[$InputFileName, 3]] 7 | 8 | BeginPackage["CodeInspector`Generate`MakeCodeAnalysisOptionsPalette`"] 9 | 10 | 11 | Begin["`Private`"] 12 | 13 | (* 14 | Do not allow PacletManager to participate in finding `Generate` files 15 | 16 | PacletManager will find e.g. CodeParser/Kernel/TokenEnum.wl when asked to find CodeParser`Generate`TokenEnum` 17 | 18 | related issues: PACMAN-54 19 | *) 20 | Block[{Internal`PacletFindFile = Null&}, 21 | 22 | Needs["CodeTools`Generate`GenerateSources`"]; 23 | 24 | (* 25 | not using PacletManager here, so be explicit about the path 26 | *) 27 | Get["CodeInspector`Kernel`LinterUI`"]; 28 | ] 29 | 30 | 31 | checkBuildDir[] 32 | 33 | 34 | (* ::Section::Closed:: *) 35 | (*Lou's addDefinitions tools*) 36 | 37 | 38 | (* ::Text:: *) 39 | (*constructInit[syms] constructs an Initialization setting with the Definition of each of the given symbols. Think of it as an opt-in, non-recursive SaveDefinitions.*) 40 | 41 | 42 | SetAttributes[constructInit, HoldAllComplete] 43 | constructInit[syms__] := 44 | Module[{init}, 45 | init = HoldComplete /@ Unevaluated[{syms}]; 46 | init = Replace[init, HoldComplete[sym_] :> 47 | ToExpression[ToString[Definition[sym], InputForm], InputForm, HoldComplete], {1}]; 48 | init = Flatten[HoldComplete @@ init, Infinity, HoldComplete]; 49 | Replace[init, HoldComplete[exprs__] :> (Initialization :> {exprs})] 50 | ] 51 | 52 | 53 | SetAttributes[addDefinitions, HoldAllComplete] 54 | 55 | 56 | (* just in case you want to prepend to an existing Initialization *) 57 | addDefinitions[DynamicModule[args__, Initialization :> init_, opts___], {syms__}(*, firstAction:Hold[_]:Hold[Null]*)] := 58 | Replace[constructInit[syms], { 59 | (Initialization :> newinit_) :> DynamicModule[args, Initialization :> ((*First[firstAction]; *)newinit; init), opts], 60 | else_ :> DynamicModule[args, Initialization :> init, opts] 61 | }] 62 | 63 | 64 | (* it's easier if there's not another init setting already *) 65 | addDefinitions[DynamicModule[args__], {syms__Symbol}] := 66 | With[{newinit = constructInit[syms]}, DynamicModule[args, newinit]] 67 | 68 | 69 | (* ::Section::Closed:: *) 70 | (*Tag Suppression Dialog*) 71 | 72 | 73 | getDisabledTags[scope_?(MatchQ[$FrontEnd | _NotebookObject | _CellObject]), inheritance_:CurrentValue] /; Or[inheritance === CurrentValue, inheritance === AbsoluteCurrentValue] := 74 | If[ 75 | (* We want to *only* interrogate tags in the given scope, but if the CodeToolsOptions Association hasn't been defined, then CurrentValue 76 | resolves the queried option from a higher scope, rather than just returning Inherited. Therefore, we have to check if CodeToolsOptions 77 | has been defined at the given scope. If not, then we're safe to just return {} for the suppressed/enabled tags. *) 78 | !MemberQ[Options[scope], CodeAssistOptions -> l1_ /; MemberQ[l1, "CodeToolsOptions" -> _]], 79 | {}, 80 | 81 | With[ 82 | {tagsPath = {CodeAssistOptions, "CodeToolsOptions", "CodeInspect", "Tags"}}, 83 | {rawTagsAssoc = inheritance[scope, tagsPath]}, 84 | 85 | (* Note on variable names: 86 | The "Tags" options are structured as either (e.g.) 87 | <|"ImplicitTimesAcrossLines" -> <|Enabled -> False|>|> 88 | or (e.g.) 89 | <|"DuplicateClauses" -> <|"If" -> <|Enabled -> False|>|>|> 90 | if there are sub-tags to the tags. 91 | The functions in the following KeyValueMap use variables "tag", "tagOptions", "tagOption" and "value" which correspond to this structure, such that: 92 | <|tag -> tagOptions|> 93 | and 94 | <|tag -> <|tagOption -> value|>|> *) 95 | 96 | (* From the raw association of tags and options, we want to find all tags for which Enabled is True or False 97 | (i.e. explicitly-specced rather than Inherited) and return an list of all such tags, where sub-tags are given by {tag, sub-tag}. *) 98 | 99 | {unflattenedTags = Catch @ KeyValueMap[ 100 | 101 | Function[{tag, tagOptions}, 102 | Catch @ Lookup[ 103 | (* If tagOptions isn't an Association, then Enabled hasn't been specced, so we can throw an empty list. *) 104 | Replace[tagOptions, Except[_Association] :> Throw[{}]], 105 | 106 | (* Look for Enabled in the tag options. *) 107 | Enabled, 108 | 109 | (* If Enabled isn't present, then we can look for option values of the form "SubTag" -> <|___, Enabled -> True/False, ___|> *) 110 | (* Return {tag, subtag} for subtags. *) 111 | {tag, #}& /@ Catch @ Keys @ Select[ 112 | tagOptions, 113 | (* Select options for which Enabled -> True/False (and are therefore subtags). *) 114 | Catch @ BooleanQ[Lookup[Replace[#, Except[_Association] :> Throw[{}]], Enabled, None]]&], 115 | 116 | (* If Enabled is present and if its value is boolean, return the tag. Otherwise discard the tag. *) 117 | Replace[#, {_?BooleanQ -> tag, _ -> Nothing}]&]], 118 | 119 | (* If the "Tags" option value isn't an association, then no tags have been enabled/disabled. So return an empty list. *) 120 | Replace[rawTagsAssoc, Except[_Association] :> Throw[{}]]]}, 121 | 122 | (* Flatten out and sort the list of tags by tag then subtag. *) 123 | SortBy[Flatten[unflattenedTags, 1], Replace[#, l:{__String} :> StringJoin[l]]&]]] 124 | 125 | 126 | (* Construct the Enabled option path for a tag or subtag. *) 127 | constructTagEnabledPath[tag_?(MatchQ[_String | {_String, _String}])] := { 128 | CodeAssistOptions, "CodeToolsOptions", "CodeInspect", "Tags", 129 | Switch[Head[tag], String, tag, List, Splice[tag]], 130 | Enabled} 131 | 132 | 133 | disabledTags[notebook_NotebookObject, cell_:None] := 134 | Replace[ 135 | Union[ 136 | Function[tag, {tag, $FrontEnd}] /@ getDisabledTags[$FrontEnd], 137 | Function[tag, {tag, notebook}] /@ getDisabledTags[notebook], 138 | If[cell === None, 139 | {}, 140 | Function[tag, {tag, cell}] /@ Flatten[getDisabledTags /@ Flatten[{cell}], 1]]], 141 | Except[_List] -> {}] 142 | 143 | 144 | togglerPane[] := 145 | With[ 146 | {notebook = InputNotebook[]}, 147 | {cell = With[{cells = SelectedCells[notebook]}, Replace[cells, Except[{__CellObject}] -> None]]}, 148 | {tags = disabledTags[notebook, cell]}, 149 | 150 | Highlighted[ 151 | Pane[ 152 | If[tags === {}, 153 | 154 | Column[ 155 | { 156 | Spacer[{1, 100}], 157 | CodeInspector`LinterUI`Private`styleData["TogglerPaletteHeadings"][ 158 | "No ignored issues affecting the selection", 159 | FontSlant -> Italic, FontSize -> 14]}, 160 | ItemSize -> {Full, 20}, 161 | Spacings -> 0, 162 | BaseStyle -> {FontSize -> 1}], 163 | 164 | Column[ 165 | { 166 | Spacer[{1, 5}], 167 | (* Column headings. *) 168 | Grid[ 169 | {{ 170 | Spacer[(* 18 *)33], 171 | Pane[CodeInspector`LinterUI`Private`styleData["TogglerPaletteHeadings"]["Issue", FontColor -> GrayLevel[.45]], ImageSize -> {(* 150 *)151, 17}], 172 | Pane[CodeInspector`LinterUI`Private`styleData["TogglerPaletteHeadings"]["Scope", FontColor -> GrayLevel[.45]], ImageSize -> {110, 17}]}}, 173 | 174 | Dividers -> {Center, False}, 175 | FrameStyle -> Directive[AbsoluteThickness[1], CodeInspector`LinterUI`Private`colorData[(* "TogglerDelim" *)"TogglerBack"]], 176 | Spacings -> {10, 0}, 177 | ItemSize -> Full, 178 | Alignment -> Left, 179 | BaseStyle -> {FontSize -> 1}], 180 | 181 | (* Rows of suppression controls. *) 182 | Pane[ 183 | Row[clearSuppressionControl @@@ tags], 184 | ImageSizeAction -> "Scrollable", 185 | Scrollbars -> {False, Automatic}, 186 | AppearanceElements -> None, 187 | Alignment -> {Center, Top}, 188 | ImageSize -> {333, 185}, 189 | FrameMargins -> {{0, 0}, {1, 1}}, 190 | BaseStyle -> {LineIndent -> 0}], 191 | 192 | Spacer[{1, 4}], 193 | 194 | DynamicModule[{hoverQ}, 195 | DynamicWrapper[ 196 | Pane[ 197 | CodeInspector`LinterUI`Private`button[ 198 | Row[{togglerClearAllButton[Dynamic[hoverQ]], Spacer[3], Style["Stop Ignoring All Issues", FontSize -> 13]}], 199 | 200 | CurrentValue[$FrontEnd, {CodeAssistOptions, "CodeToolsOptions", "CodeInspect", "Tags"}] = Inherited; 201 | CurrentValue[InputNotebook[], {CodeAssistOptions, "CodeToolsOptions", "CodeInspect", "Tags"}] = Inherited; 202 | CurrentValue[SelectedCells[InputNotebook[]], {CodeAssistOptions, "CodeToolsOptions", "CodeInspect", "Tags"}] = Inherited; 203 | CodeInspector`LinterUI`Private`togglerTickle = RandomReal[], 204 | 205 | FrameMargins -> {{5.5, 8}, {2, 0}}], 206 | FrameMargins -> {{6, 0}, {3, 3}}], 207 | 208 | hoverQ = CurrentValue["MouseOver"]]]}, 209 | 210 | BaseStyle -> {FontSize -> 1, FontColor -> RGBColor[0,0,0,0]}, 211 | Spacings -> {0, {0, 0, 4, {0}}}, 212 | ItemSize -> {0, 0}, Spacings -> 0, 213 | Dividers -> {None, {3 -> GrayLevel[.8]}}]], 214 | 215 | ImageSize -> {335, 243}, 216 | Alignment -> {Center, Top}], 217 | 218 | Background -> CodeInspector`LinterUI`Private`colorData["TogglerBack"], 219 | Frame -> True, 220 | FrameStyle -> Directive[AbsoluteThickness[1], CodeInspector`LinterUI`Private`colorData["TogglerPodEdge"]], 221 | FrameMargins -> None, 222 | RoundingRadius -> 3]] 223 | 224 | 225 | togglerClearAllButton[Dynamic[hoverQ_]] := 226 | Graphics[ 227 | { 228 | CapForm["Round"], AbsoluteThickness[1.5], 229 | Dynamic[If[hoverQ, CodeInspector`LinterUI`Private`colorData["TogglerEdgeHover"], CodeInspector`LinterUI`Private`colorData["TogglerCross"]]], 230 | Line[{{{-1, -1}, {1, 1}}, {{-1, 1}, {1, -1}}}]}, 231 | ImageSize -> 15{1, 1}, PlotRangePadding -> 1.75, PlotRange -> 1, BaselinePosition -> Scaled[.2]] 232 | 233 | 234 | clearSuppressionControl[tag_?(MatchQ[_String | {_String, _String}]), scope_?(MatchQ[_FrontEndObject | _NotebookObject | _CellObject | {___CellObject}])] := 235 | With[ 236 | { 237 | (* The tag name. For subtags, insert a triangle between the tag and subtag. *) 238 | tagText = Replace[tag, l_List :> StringJoin[Insert[l, "\[VeryThinSpace]\:25bb\[VeryThinSpace]", 2]]], 239 | 240 | (* The scope. *) 241 | scopeText = Switch[scope, 242 | _FrontEndObject, 243 | "All Notebooks", 244 | _NotebookObject, 245 | FE`Evaluate[ 246 | FEPrivate`TruncateStringToWidth[ 247 | AbsoluteCurrentValue[scope, WindowTitle], 248 | "DialogStyle", 105]], 249 | _CellObject, 250 | "Selected Cell", 251 | _, 252 | "Selected Cells"] 253 | }, 254 | 255 | DynamicModule[ 256 | {clearedQ = False, hoverQ = False}, 257 | PaneSelector[ 258 | { 259 | False -> Button[ 260 | DynamicWrapper[ 261 | Highlighted[ 262 | Grid[ 263 | {{ 264 | (* Display the remove icon (a cross, which changes colour on mouseover to match the control's frame). *) 265 | togglerClearAllButton[Dynamic[hoverQ]], 266 | 267 | (* Display the tag name. *) 268 | Pane[CodeInspector`LinterUI`Private`styleData["TogglerTagText"][tagText], ImageSize -> {150, 20}, BaselinePosition -> Baseline, Alignment -> {Left, Center}], 269 | 270 | (* Display the scope. *) 271 | Pane[scopeText, ImageSize -> {110, 20}, BaselinePosition -> Baseline, Alignment -> {Left, Center}] 272 | }}, 273 | 274 | ItemSize -> Full, 275 | Alignment -> {Left, Baseline}, 276 | Dividers -> {Center, False}, 277 | FrameStyle -> Directive[AbsoluteThickness[1], CodeInspector`LinterUI`Private`colorData["TogglerDelim"]], 278 | BaseStyle -> {FontFamily -> "Source Sans Pro", FontSize -> 13, FontColor -> CodeInspector`LinterUI`Private`colorData["TogglerText"]}], 279 | 280 | Background -> Dynamic[If[hoverQ, CodeInspector`LinterUI`Private`colorData["TogglerBackHover"], CodeInspector`LinterUI`Private`colorData["TogglerBack"]]], 281 | RoundingRadius -> Dynamic[If[hoverQ, 3, 0]], 282 | Frame -> True, FrameMargins -> {{2, 2}, {0, 0}}, 283 | FrameStyle -> Dynamic[If[hoverQ, CodeInspector`LinterUI`Private`colorData["TogglerEdgeHover"], CodeInspector`LinterUI`Private`colorData["TogglerEdge"]]]], 284 | 285 | hoverQ = CurrentValue["MouseOver"]], 286 | 287 | (* Button action. Clear the suppression, and hide the control by setting clearedQ to True. *) 288 | CurrentValue[scope, constructTagEnabledPath[tag]] = Inherited; 289 | clearedQ = True; 290 | CodeInspector`LinterUI`Private`togglerTickle = RandomReal[], 291 | 292 | Appearance -> None, 293 | Tooltip -> "Stop ignoring issue", 294 | TooltipDelay -> 0], 295 | 296 | 297 | True -> Pane[Spacer[{0,0}], ImageSize -> {0, 0}, ImageMargins -> {{0, 0}, {-1, 0}}]}, 298 | 299 | Dynamic[clearedQ]]]] 300 | 301 | (* ::Section::Closed:: *) 302 | (*Write*) 303 | 304 | generatePalette[] := 305 | Module[{togglerPalette, res}, 306 | 307 | Print["UsingFrontEnd... \[WatchIcon]"]; 308 | 309 | UsingFrontEnd[ 310 | 311 | togglerPalette = 312 | CreatePalette[ 313 | addDefinitions[ 314 | DynamicModule[{}, 315 | Column[{ 316 | Spacer[{1, 10}], 317 | Row[{Spacer[8], CodeInspector`LinterUI`Private`styleData["TogglerPaletteSectionHeadings"]["Ignored Issues"]}], 318 | Spacer[{1, 4}], 319 | Pane[ 320 | Dynamic[ 321 | CodeInspector`LinterUI`Private`togglerTickle; 322 | With[{nb = InputNotebook[]}, AbsoluteCurrentValue[nb, "SelectionHasUpdatedStyles"]]; 323 | Dynamic[togglerPane[], SingleEvaluation -> True, Background -> Hue[RandomReal[], 0, 0, 0]]], 324 | FrameMargins -> {{8, 8}, {0, 0}}, 325 | Alignment -> Center], 326 | Spacer[{1, 3}]}, 327 | 328 | BaseStyle -> {FontSize -> 1}, 329 | Spacings -> 1]], 330 | { 331 | getDisabledTags, 332 | constructTagEnabledPath, 333 | disabledTags, 334 | togglerPane, 335 | togglerClearAllButton, 336 | clearSuppressionControl, 337 | CodeInspector`LinterUI`Private`colorData, 338 | CodeInspector`LinterUI`Private`styleData, 339 | CodeInspector`LinterUI`Private`button 340 | }], 341 | 342 | Background -> GrayLevel[1], 343 | WindowTitle -> "Code Analysis Options", 344 | MenuSortingValue -> 1150, (* Group the Code palettes together -- 416653 *) 345 | Saveable -> False, 346 | Evaluator -> "System"]; 347 | 348 | If[!MatchQ[togglerPalette, _NotebookObject], 349 | Print["CreatePalette failed: ", togglerPalette]; 350 | Quit[1] 351 | ]; 352 | 353 | Quiet[DeleteFile[FileNameJoin[{buildDir, "paclet", "CodeInspector", "FrontEnd", "Palettes", "CodeAnalysisOptions.nb"}]], DeleteFile::fdnfnd]; 354 | 355 | Print["saving CodeAnalysisOptions.nb"]; 356 | res = NotebookSave[togglerPalette, FileNameJoin[{buildDir, "paclet", "CodeInspector", "FrontEnd", "Palettes", "CodeAnalysisOptions.nb"}]]; 357 | 358 | Print[res]; 359 | 360 | If[res =!= Null, 361 | Quit[1] 362 | ]; 363 | ]; 364 | 365 | (* 366 | NotebookSave may fail, but give no indication, 367 | so need to explicitly check that file was created 368 | bug 429251 369 | *) 370 | If[!FileExistsQ[FileNameJoin[{buildDir, "paclet", "CodeInspector", "FrontEnd", "Palettes", "CodeAnalysisOptions.nb"}]], 371 | Quit[1] 372 | ]; 373 | 374 | Print["Done UsingFrontEnd"]; 375 | ] 376 | 377 | generate[] := ( 378 | 379 | Print["Generating Palette..."]; 380 | 381 | generatePalette[]; 382 | 383 | Print["Done Palette"] 384 | ) 385 | 386 | If[!StringQ[script], 387 | Quit[1] 388 | ] 389 | If[AbsoluteFileName[script] === AbsoluteFileName[$InputFileName], 390 | generate[] 391 | ] 392 | 393 | (* ::Section::Closed:: *) 394 | (*Package Footer*) 395 | 396 | 397 | End[] 398 | 399 | EndPackage[] 400 | --------------------------------------------------------------------------------