|
| 1 | +/** |
| 2 | + * Provides classes and predicates for classifying files as containing |
| 3 | + * generated code, test code, externs declarations, library code or |
| 4 | + * template code. |
| 5 | + */ |
| 6 | + |
| 7 | +import semmle.javascript.GeneratedCode |
| 8 | +import semmle.javascript.frameworks.Testing |
| 9 | +import semmle.javascript.frameworks.Templating |
| 10 | +import semmle.javascript.dependencies.FrameworkLibraries |
| 11 | + |
| 12 | +/** |
| 13 | + * Holds if `e` may be caused by parsing a template file as plain HTML or JavaScript. |
| 14 | + * |
| 15 | + * We use two heuristics: check for the presence of a known template delimiter preceding |
| 16 | + * the error on the same line, and check whether the file name contains `template` or |
| 17 | + * `templates`. |
| 18 | + */ |
| 19 | +predicate maybeCausedByTemplate(JSParseError e) { |
| 20 | + exists(File f | f = e.getFile() | |
| 21 | + e.getLine().indexOf(Templating::getADelimiter()) <= e.getLocation().getStartColumn() |
| 22 | + or |
| 23 | + f.getAbsolutePath().regexpMatch("(?i).*\\btemplates?\\b.*") |
| 24 | + ) |
| 25 | +} |
| 26 | + |
| 27 | +/** |
| 28 | + * Holds if `e` is an expression in the form `o.p1.p2.p3....pn`. |
| 29 | + */ |
| 30 | +private predicate isNestedDotExpr(DotExpr e) { |
| 31 | + e.getBase() instanceof VarAccess or |
| 32 | + isNestedDotExpr(e.getBase()) |
| 33 | +} |
| 34 | + |
| 35 | +/** |
| 36 | + * Holds if `tl` only contains variable declarations and field reads. |
| 37 | + */ |
| 38 | +private predicate looksLikeExterns(TopLevel tl) { |
| 39 | + forex(Stmt s | s.getParent() = tl | |
| 40 | + exists(VarDeclStmt vds | vds = s | |
| 41 | + forall(VariableDeclarator vd | vd = vds.getADecl() | not exists(vd.getInit())) |
| 42 | + ) |
| 43 | + or |
| 44 | + isNestedDotExpr(s.(ExprStmt).getExpr()) |
| 45 | + ) |
| 46 | +} |
| 47 | + |
| 48 | +/** |
| 49 | + * Holds if `f` is classified as belonging to `category`. |
| 50 | + * |
| 51 | + * There are currently four categories: |
| 52 | + * - `"generated"`: `f` contains generated or minified code; |
| 53 | + * - `"test"`: `f` contains test code; |
| 54 | + * - `"externs"`: `f` contains externs declarations; |
| 55 | + * - `"library"`: `f` contains library code; |
| 56 | + * - `"template"`: `f` contains template code. |
| 57 | + */ |
| 58 | +predicate classify(File f, string category) { |
| 59 | + isGenerated(f.getATopLevel()) and category = "generated" |
| 60 | + or |
| 61 | + ( |
| 62 | + exists(Test t | t.getFile() = f) |
| 63 | + or |
| 64 | + exists(string stemExt | stemExt = "test" or stemExt = "spec" | |
| 65 | + f = getTestFile(any(File orig), stemExt) |
| 66 | + ) |
| 67 | + or |
| 68 | + f.getAbsolutePath().regexpMatch(".*/__(mocks|tests)__/.*") |
| 69 | + ) and |
| 70 | + category = "test" |
| 71 | + or |
| 72 | + (f.getATopLevel().isExterns() or looksLikeExterns(f.getATopLevel())) and |
| 73 | + category = "externs" |
| 74 | + or |
| 75 | + f.getATopLevel() instanceof FrameworkLibraryInstance and category = "library" |
| 76 | + or |
| 77 | + exists(JSParseError err | maybeCausedByTemplate(err) | |
| 78 | + f = err.getFile() and category = "template" |
| 79 | + ) |
| 80 | + or |
| 81 | + // Polymer templates |
| 82 | + exists(HTML::Element elt | elt.getName() = "template" | |
| 83 | + f = elt.getFile() and category = "template" |
| 84 | + ) |
| 85 | +} |
0 commit comments