Skip to content
This repository has been archived by the owner on Apr 1, 2022. It is now read-only.

Merge rebar aliases into analyzer #139

Merged
merged 2 commits into from
Sep 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# v2.3.1

- RPM: Merge spec file results in the analyzer. ([#138](https://github.com/fossas/spectrometer/pull/138))
- Erlang: Resolve rebar3 aliased packages to their true names. ([#139](https://github.com/fossas/spectrometer/pull/139))
- Gradle: Accept and tag all build configuration names. ([#134](https://github.com/fossas/spectrometer/pull/134))

# v2.3.0
Expand Down
10 changes: 8 additions & 2 deletions src/Strategy/Erlang/ConfigParser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ module Strategy.Erlang.ConfigParser
parseCharNum,
parseIntLiteral,
parseFloatLiteral,
atom,
AtomText (..),
ErlValue (..),
ConfigValues (..),
intLiteralInBase,
alphaNumToInt,
)
Expand All @@ -34,6 +36,7 @@ import qualified Text.Megaparsec.Char.Lexer as L
type Parser = Parsec Void Text

newtype AtomText = AtomText {unAtomText :: Text} deriving (Eq, Ord, Show, ToJSON)
newtype ConfigValues = ConfigValues {unConfigValues :: [ErlValue]} deriving (Eq, Ord, Show)

data ErlValue
= ErlAtom AtomText
Expand All @@ -57,8 +60,11 @@ instance ToJSON ErlValue where
alphaNumSeq :: [Char]
alphaNumSeq = ['0' .. '9'] <> ['A' .. 'Z']

parseConfig :: Parser [ErlValue]
parseConfig = scn *> parseTuple `endBy1` symbol "."
atom :: Text -> ErlValue
zlav marked this conversation as resolved.
Show resolved Hide resolved
atom = ErlAtom . AtomText

parseConfig :: Parser ConfigValues
parseConfig = ConfigValues <$ scn <*> parseTuple `endBy1` symbol "."

parseErlValue :: Parser ErlValue
parseErlValue = parseErlArray <|> parseTuple <|> parseNumber <|> parseErlString <|> parseAtom
Expand Down
46 changes: 41 additions & 5 deletions src/Strategy/Erlang/Rebar3Tree.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}

module Strategy.Erlang.Rebar3Tree
( discover
Expand All @@ -13,21 +14,24 @@ module Strategy.Erlang.Rebar3Tree
import Control.Effect.Diagnostics
import Data.Foldable (find)
import qualified Data.Map.Strict as M
import Data.Maybe (mapMaybe)
import Data.Text (Text)
import qualified Data.Text as T
import Data.Void (Void)
import DepTypes
import Discovery.Walk
import Effect.Exec
import Effect.ReadFS
import Graphing (Graphing, unfold)
import Path
import Strategy.Erlang.ConfigParser (parseConfig, ErlValue (..), ConfigValues (..), AtomText (..))
import Text.Megaparsec
import Text.Megaparsec.Char
import Types

discover :: HasDiscover sig m => Path Abs Dir -> m ()
discover = walk $ \dir _ files -> do
case find (\f -> (fileName f) == "rebar.config") files of
case find (\f -> fileName f == "rebar.config") files of
Nothing -> pure WalkContinue
Just _ -> do
runSimpleStrategy "erlang-rebar3tree" ErlangGroup $ analyze dir
Expand All @@ -40,8 +44,41 @@ rebar3TreeCmd = Command
, cmdAllowErr = Never
}

analyze :: (Has Exec sig m, Has Diagnostics sig m) => Path Abs Dir -> m ProjectClosureBody
analyze dir = mkProjectClosure dir <$> execParser rebar3TreeParser dir rebar3TreeCmd
configFile :: Path Rel File
configFile = $(mkRelFile "rebar.config")

analyze :: (Has Exec sig m, Has ReadFS sig m, Has Diagnostics sig m) => Path Abs Dir -> m ProjectClosureBody
analyze dir = do
aliasMap <- extractAliasLookup <$> readContentsParser parseConfig (dir </> configFile)
mkProjectClosure dir . unaliasDeps aliasMap <$> execParser rebar3TreeParser dir rebar3TreeCmd

extractAliasLookup :: ConfigValues -> M.Map Text Text
extractAliasLookup (ConfigValues erls) = foldr extract M.empty erls
where
extract :: ErlValue -> M.Map Text Text -> M.Map Text Text
extract val aliasMap = aliasMap <> M.fromList (mapMaybe getAlias packages)
where
packages :: [ErlValue]
packages = case val of
ErlTuple [ErlAtom (AtomText "deps"), ErlArray deplist] -> deplist
_ -> []

getAlias :: ErlValue -> Maybe (Text, Text)
getAlias erl = case erl of
ErlTuple [ErlAtom (AtomText realname), ErlString _, ErlTuple [ErlAtom (AtomText "pkg"), ErlAtom (AtomText alias)]] -> Just (realname, alias)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern match is a beast, but it actually seems like the most idiomatic thing. It's kind of a weird structure to find, functionally, so expressing it as-is seems to make sense.

ErlTuple [ErlAtom (AtomText realname), ErlTuple [ErlAtom (AtomText "pkg"), ErlAtom (AtomText alias)]] -> Just (realname, alias)
_ -> Nothing


unaliasDeps :: M.Map Text Text -> [Rebar3Dep] -> [Rebar3Dep]
unaliasDeps aliasMap = map unalias
where
unalias :: Rebar3Dep -> Rebar3Dep
unalias dep = changeName dep . lookupName aliasMap $ depName dep
lookupName :: M.Map Text Text -> Text -> Text
lookupName map' name = M.findWithDefault name name map'
changeName :: Rebar3Dep -> Text -> Rebar3Dep
changeName dep name = dep { depName = name }

mkProjectClosure :: Path Abs Dir -> [Rebar3Dep] -> ProjectClosureBody
mkProjectClosure dir deps = ProjectClosureBody
Expand Down Expand Up @@ -124,5 +161,4 @@ rebar3TreeParser = concat <$> ((try (rebarDep 0) <|> ignoredLine) `sepBy` eol) <
rebarRecurse :: Int -> Parser [Rebar3Dep]
rebarRecurse depth = do
_ <- chunk "\n"
deps <- rebarDep depth
pure deps
rebarDep depth
30 changes: 14 additions & 16 deletions test/Erlang/ConfigParserSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ import Text.Megaparsec
parseMatch :: (Show a, Eq a) => Parsec Void Text a -> Text -> a -> Expectation
parseMatch parser input expected = parse parser "" input `shouldParse` expected

atom :: Text -> ErlValue
atom = ErlAtom . AtomText

spec :: Spec
spec = do
describe "Erlang config parser" $ do
Expand Down Expand Up @@ -104,19 +101,20 @@ spec = do

it "should parse everything at once" $
parse parseConfig "stresstest.config" oneWithEverything `shouldParse`
[ErlTuple [
atom "rawAtom",
atom "quotedAtom",
ErlString "Regular String",
ErlString "Escaped \" String",
ErlInt 1234, -- Literal
ErlFloat 3.14159,
ErlInt 120, -- '$x'
ErlInt 35338, -- 'wes' in base 33
ErlArray [atom "arr1"],
ErlArray [ErlTuple [atom "key", ErlString "value"]],
ErlTuple [atom "number", ErlInt 5678] -- Literal
]]
ConfigValues
[ErlTuple [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Would it make sense to have multiple ErlTuple values in the ConfigValues array for the test now that we made that possible with the ConfigValues type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand. It already supports multiple ErlValues.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea that makes sense, I was asking if we should modify the test values to contain multiple ErlValues so that we can test it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, the real-life test already checks that. It fails if the parser doesn't sit just right. Check here

atom "rawAtom",
atom "quotedAtom",
ErlString "Regular String",
ErlString "Escaped \" String",
ErlInt 1234, -- Literal
ErlFloat 3.14159,
ErlInt 120, -- '$x'
ErlInt 35338, -- 'wes' in base 33
ErlArray [atom "arr1"],
ErlArray [ErlTuple [atom "key", ErlString "value"]],
ErlTuple [atom "number", ErlInt 5678] -- Literal
]]

describe "radix parser" $
it "should parse number strings correctly" $ do
Expand Down